从源码解析Android异步消息处理机制(二)

从源码解析Android异步消息处理机制(二)


前言:本篇纯属个人学习笔记,故摘录《Android开发艺术探索》大量语句,特此声明



Handler是Android消息机制的上层接口,对于Handler的使用我们是熟悉的,我们往往认为Handler的作用仅仅是更新UI,但其实这只是Handler的一个特殊的使用场景。Handler的主要作用是将一个任务切换到某个指定的线程中去执行。
当然我们常用Handler来更新UI是有原因的:这是因为Android规定访问UI只能在主线程(UI线程)中进行,如果在子线程中访问UI,那么程序会抛出异常。ViewRootImpl(Android.View.ViewRootImpl).checkThread()方法对UI操作做了验证

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,但又不能在主线程中进行耗时操作,否则会导致ANR(程序无响应)。这时候就是Handler起作用的时候了。

延伸:系统为什么不允许在子线程中访问UI?这是因为Android的UI控件不是线程安全的

当多个线程访问一个类时,若不用考虑这些线程在运行时环境下的调整和交替执行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么称这个类是线程安全的。

所以在多线程中并发访问可能会导致UI控件处于不可预期的状态。对于多线程操作我们可以使用锁机制,但是系统不对UI控件的访问加上锁机制是有原因的:①会增加逻辑的复杂性;②会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。
首先我们看看Handler的默认构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

从上面我们可以看出如果当前线程没有Looper的话,就会抛出异常,这也就解释了在没有Looper的子线程中创建Handler会引发程序异常的原因了。紧接着又获取mLooper实例中保存的mQueue,这样Handler就与MessageQueue关联上了。
接下来我们啃一下我们最常用的方法——sengMessage(msg)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

辗转反侧到了enqueueMessage()方法中,首先为msg.target赋值为this,而在Looper的loop方法会取出每个msg,并交给msg.target.dispatchMessage(msg)去处理消息,也就是将当前的handler作为msg的target属性。最终调用queue的enqueueMessage的方法。简而言之,Handler发送消息的过程仅仅是向消息队列中插入一条消息。而消息由Looper交由Handler处理(dispatchMessage())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void dispatchMessage(Message msg) {  
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

handler处理消息的流程如下:
首先检查Message的callback是否为null, 不为null就通过handleCallback(msg)来处理消息。handleCallback()实现如下:

1
2
3
private static void handleCallback(Message message) {
message.callback.run();
}

message的callback是一个Runnable对象,实际上就是Handler的post传递的Runnable参数。其次检查mCallback是否为null,不为null就调用mCallback的handleMessage()方法。
mCallback是一个接口:

1
2
3
4
5
6
7
8
9
10
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}

最后还是调到handleMessage()方法。这就给我们提供了另一种方式来创建Handler对象,就如源码中的注释。
handleMessage()是一个空方法,消息的最终回调是由我们控制的,我们在创建Handler的时候都是复写handlerMessage方法,然后根据msg.what去处理消息的。

当然,在使用Handler时,应注意内存泄漏问题,相关解决办法可自行google。

小结

  1. Handler的构造方法会获取当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue关联上。
  2. Handler的sendMessage()会将当前的handler作为msg的target属性。最终调用queue的enqueueMessage的方法。简而言之,Handler发送消息的过程仅仅是向消息队列中插入一条消息。而消息由Looper交由Handler处理(dispatchMessage())。