Android异步消息处理机制概述

       Android 的消息机制主要是指 Handler 的运行机制,以及 Handler 所附带的 MessageQueue 和 Looper 的工作过程,这三者实际上是一个整体,只不过我们在开发的过程中比较多地接触到 Handler 而已。Handler 的主要作用是将一个任务切换到某个指定的线程中去执行,那么 Android 为什么要提供这个功能呢?或者说 Android 为什么需要提供在某个具体的线程中执行任务这种功能呢?这是因为 Android 规定访问 UI 只能在主线程中进行,如果在子线程中访问 UI,那么程序就会抛出异常。ViewRootImpl 对 UI 操作做了验证,这个验证工作是由 ViewRootImpl的 checkThread 方法来完成的,如下所示:

1
2
3
4
5
6
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}

       由于这一点的限制,导致必须在主线程中访问 UI,但是 Android 又建议不要在主线程中进行耗时操作,否则会导致程序无法响应即 ANR。考虑一种情况,假如我们需要从服务端拉取一些信息并将其显示在 UI 上,这个时候必须在子线程中进行拉取工作,拉取完毕后又不能在子线程中直接访问 UI,如果没有 Handler,那么我们的确没有办法将访问 UI 的工作切换到主线程中去执行。因此,系统之所以提供 Handler,主要原因就是为了解决在子线程中无法访问 UI 的矛盾。

       这里再延伸一点,系统为什么不允许在子线程中访问 UI 呢?这是因为 Android 的 UI 控件不是线程安全的,如果在多线程中并发访问可能会导致 UI 控件处于不可预期的状态,那为什么系统不对 UI 控件的访问加上锁机制呢?缺点有两个:首先加上锁机制会让 UI 访问的逻辑变得复杂;其次锁机制会降低 UI 访问的效率,因为锁机制会阻塞某些线程的执行。鉴于这两个缺点,最简单且高效的方法就是采用单线程模型来处理 UI 操作,对于开发者来说也不是很麻烦,只是需要通过 Handler 切换一下 UI 访问的执行线程即可。

       先简单描述一下 Handler 的工作原理。Handler 创建时会采用当前线程的 Looper 来构建内部的消息循环系统,如果当前线程没有 Looper,那么就会报错。所以需要为当前线程创建 Looper,或者在一个有 Looper 的线程中创建 Handler。

       Handler 创建完毕后,这个时候其内部的 Looper 以及 MessageQueue 就可以和 Handler 一起协同工作了,然后通过 Handler 的 post 方法将一个 Runnable 投递到 Handler 内部的 Looper 中去处理,也可以通过 Handler 的 send 方法发送一个消息,这个消息同样会在 Looper 中去处理。其实 post 方法最终也是通过 send 方法来完成的,接下来主要来看一下 send 方法的工作过程。当 Handler 的 send 方法被调用时,它会调用 MessageQueue 的 enqueueMessage 方法将这个消息放入消息队列中,然后 Looper 发现有新消息到来时,就会处理这个消息,最终消息中的 Runnable 或者 Handler 的 handleMessage 方法就会被调用。注意 Looper 是运行在创建 Handler 所在的线程中的,这样一来 Handler 中的业务逻辑就被切换到创建 Handler 所在的线程中去执行了。

参考资料:
《Android 开发艺术探索》任玉刚 第10章 Android的消息机制 10.1 Android消息机制概述

Fork me on GitHub