Android 异步消息处理机制

运作机制

Android 中的异步消息处理主要由 4 个部分组成:Message、Handler、MessageQueue 和 Looper。

  1. Message

    Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间传递数据。

  2. Handler

    主要是用于发送和处理消息的。发送消息一般是使用 Handler 的 sendMessage() 方法、post() 方法等,而发出的消息经过一系列地辗转处理后,最终会传递到 Handler 的 handleMessage() 方法中。

  3. MessageQueue

    MessageQueue 是消息队列的意思,它主要用于存放所有通过 Handler 发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个 MessageQueue 对象。

    负责消息的存储与管理,负责管理由 Handler 发送过来的 Message。读取会自动删除消息,单链表维护,插入和删除上比队列有优势。在其 next () 方法中会无限循环,不断判断是否有消息,有就返回这条消息并移除。

  4. Looper

    Looper 是每个线程中的 MessageQueue 的管家,调用 Looper 的 loop() 方法后,就会进入一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息时,就会将它取出,并传递到 Handler 的 handleMessage() 方法中。每个线程中只会有一个 Looper 对象。

    负责关联线程以及消息的分发,在该线程下从 MessageQueue 获取 Message,分发给 Handler,Looper 创建的时候会创建一个 MessageQueue,调用 loop () 方法的时候消息循环开始,其中会不断调用 messageQueue 的 next () 方法,当有消息就处理,否则阻塞在 messageQueue 的 next () 方法中。当 Looper 的 quit () 被调用的时候会调用 messageQueue 的 quit (),此时 next () 会返回 null,然后 loop () 方法也就跟着退出。

图 1

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

val updateText = 1
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
updateText -> binding.textView.text = "Nice to meet you"
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.changeTextBtn.setOnClickListener {
thread {
val msg = Message()
msg.what = updateText
handler.sendMessage(msg)
}
}
}
}

Handler

Handler 源码

Handler.java
public class Handler {

/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}

/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
// 判断callback是否为空,不为空调用handleCallback()处理消息。callback:Runnable
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

......

public Handler(@Nullable Callback callback, boolean async) {

......

// 创建一个Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
// Handler要正常工作,当前线程中必须有一个指定的Looper对象
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 创建消息队列,Looper内部创建好之后赋值给Handler内部成员变量,这意味着Handler和Looper共用一个消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

......

// 将Handler发送的消息添加到消息队列中
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 将当前Handler对象绑定到Message内部的Target变量当中
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();

if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

......

@NonNull
public final Looper getLooper() {
return mLooper;
}

......

private static void handleCallback(Message message) {
// 调用Runnable的run()方法在子线程进行操作
message.callback.run();
}

@UnsupportedAppUsage
final Looper mLooper;
final MessageQueue mQueue;
@UnsupportedAppUsage
final Callback mCallback;
final boolean mAsynchronous;
@UnsupportedAppUsage
IMessenger mMessenger;

}

MessageQueue

MessageQueue 源码

public final class MessageQueue {

......

// 读取,会将消息从消息队列中删除
@UnsupportedAppUsage
Message next() {
......
}

......

// 向消息队列中插入消息
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}

......
return true;
}

......

}

Looper

Looper 源码

public final class Looper {

......

public static void prepare() {
prepare(true);
}

// 创建Looper对象
private static void prepare(boolean quitAllowed) {
// 通过ThreadLocal容器存放Looper对象,可以确保每一个线程获取到的Looper都是唯一的。
// ThreadLocal也被叫做线程本地变量,它能为每个变量在每个线程创建一个副本,每个线程都可以访问自己内部副本的变量,不用访问其他线程的变量从而导致不同线程变量不一致。
if (sThreadLocal.get() != null) {
// 判断ThreadLocal中是否已经存在Looper,不为空表示Looper已经创建好了
throw new RuntimeException("Only one Looper may be created per thread");
}
// 将没有创建好线程的Looper对象放到ThreadLocal容器中以便下次直接使用
sThreadLocal.set(new Looper(quitAllowed));
}

@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}

/**
* Set the transaction observer for all Loopers in this process.
*
* @hide
*/
public static void setObserver(@Nullable Observer observer) {
sObserver = observer;
}

/**
* Poll and deliver single message, return true if the outer loop should continue.
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}

......

try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

......

return true;
}

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
public static void loop() {
// 获取Looper对象
final Looper me = myLooper();
// 判断Looper对象是否为空
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}

......

// 死循环,Looper轮询器会不断地从消息队列中获取消息
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}

......

/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}

private Looper(boolean quitAllowed) {
// 创建Looper对象时MessageQueue对象也被创建好了,保证Looper对象中持有MessageQueue对象
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

......

}

技巧

创建 Message 实例的最佳方式

由于 Handler 极为常用,所以为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候尽量复用 Message,减少内存消耗。

方法有二:

  1. 通过 Message 的静态方法 Message.obtain()  获取;

  2. 通过 Handler 的公有方法 handler.obtainMessage() 。

判断当前线程是否是主线程

Looper.getMainLooper().thread == Thread.currentThread()

总结

  • Looper 类主要是为每个线程开启的单独的消息循环

    默认情况下 Android 中新诞生的线程没有开启消息循环,在线程中使用 Handler 要先调用 Looper.prepare() 方法,主线程除外,因为系统已经自动创建了 Looper 对象

  • Handler 可以看作是 Looper 的一个接口,用来向指定的 Looper 中的 MessageQueue 发送消息

  • 非主线程中不能直接 new Handler (),因为 handler 需要发送消息到 MessageQueue,线程中没有 MessageQueue,而 MessageQueue 是由 Looper 管理的,在该线程中创建好 Looper 才能 new Handler

参考文章

Handler 都没搞懂,拿什么去跳槽啊?

AsyncTask

基本用法

由于 AsyncTask 是一个抽象类,所以如果我们想使用它,就必须创建一个子类去继承它。在继承时我们可以为 AsyncTask 类指定 3 个泛型参数,这 3 个参数的用途如下。

  • Params。在执行 AsyncTask 时需要传入的参数,可用于在后台任务中使用。

  • Progress。在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。

  • Result。当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

经常需要重写的方法有以下 4 个。

  1. onPreExecute()

    这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

  2. doInBackground(Params…)

    这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成,就可以通过 return 语句将任务的执行结果返回,如果 AsyncTask 的第三个泛型参数指定的是 Unit,就可以不返回任务执行结果。注意,在这个方法中是不可以进行 UI 操作的,如果需要更新 UI 元素,比如说反馈当前任务的执行进度,可以调用 publishProgress (Progress…) 方法来完成。

  3. onProgressUpdate(Progress…)

    当在后台任务中调用了 publishProgress (Progress…) 方法后,onProgressUpdate (Progress…) 方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对 UI 进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

  4. onPostExecute(Result)

    当后台任务执行完毕并通过 return 语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些 UI 操作,比如说提醒任务执行的结果,以及关闭进度条对话框等。

因此,一个比较完整的自定义 AsyncTask 就可以写成如下形式:

class DownloadTask : AsyncTask<Unit, Int, Boolean>() {
override fun onPreExecute() {
progressDialog.show() // 显示进度对话框
}

override fun doInBackground(vararg params: Unit?) = try {
while (true) {
val downloadPercent = doDownload() // 这是一个虚构的方法
publishProgress(downloadPercent)
if (downloadPercent >= 100) {
break
}
}
true
} catch (e: Exception) {
false
}

override fun onProgressUpdate(vararg values: Int?) {
// 在这里更新下载进度
progressDialog.setMessage("Downloaded ${values[0]}%")
}

override fun onPostExecute(result: Boolean) {
progressDialog.dismiss()// 关闭进度对话框
// 在这里提示下载结果
if (result) {
Toast.makeText(context, "Download succeeded", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, " Download failed", Toast.LENGTH_SHORT).show()
}
}
}

如果想要启动这个任务,只需编写以下代码即可:

DownloadTask().execute()

注意要点

  • AsyncTask 的实例必须在主线程中创建

  • AsyncTask 的 execute 方法必须在主线程中调用

  • 四个回调方法会自动调用

  • 一个 AsyncTask 的实例,只能执行一次 execute 方法