Handler 消息机制

Android 通过Binder机制来解决进程间通信问题,而通过Handler消息机制来解决线程之间通信问题,或者用来切换线程。毕竟Android要求只能主线程更新界面,对于耗时操作和网络请求都需要在其他线程执行,就涉及到线程切换。

Android应用都是靠消息来驱动工作的。大致原理就是:

  • 一个消息队列,往消息队列投递信息。
  • 一个消息循环,不断从消息队列取消息,然后处理。

而这个工作原理就涉及到三个类:LooperMessageQueueHandler

Looper

Looper的创建需要通过Looper.prepare函数来创建。

1
2
3
4
5
6
7
8
9
10
public static void prepare() {
prepare(true);
}

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

这里的sThreadLocal对象是ThreadLocal类型,用于存储线程私有全局变量,具体可看ThreadLocal解析。也就是在线程任何地方获取到的Looper对象都是同一个。通过判断语句来看,一个线程中只能调用一次Looper.prepare创建一次Looper对象。通过Looper的构造函数直接创建了Looper对象。

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

Looper的构造函数中创建了一个消息队列MessageQueue的实例mQueue,并持有当前线程对象的引用mThread。也就是说prepare函数创建了Looper对象,且该对象持有一个消息队列。并将Looper对象设置到线程ThreadLocal中,保证每条线程只有一个Looper对象。

有了消息队列,就要让消息队列跑起来,在调用了prepare函数,再调用Looper.loop函数,开始消息循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}

myLooper函数通过ThreadLocal对象sThreadLocal获取Looper对象,所以prepare函数需要在loop函数之前调用,否则会抛出异常。

然后在for死循环中,不断调用消息队列MessageQueue对象queuenext函数来获取下一个待处理的消息Message对象message。这里的消息队列来自Looper对象,即在创建Looper对象创建的MessageQueue对象。

然后通过Message对象的target对象分发处理该消息。最后对消息进行回收处理。那这里的target对象是谁?也就是Handler对象了。

Handler

Handler可以理解为消息机制里的辅助类,因为此时消息队列有了,消息循环也有,我们可以手动去队列取消息和往队里方向,但这容易出错。所以通过Handler来投递信息和处理信息。

Handler一共7个构造函数,其中两个已经标记过时了。

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
32
33
//设置Looper
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
//设置Looper和Callback
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
//设置同步屏障
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Handler(boolean async) {
this(null, async);
}
//设置回调和同步
public Handler(@Nullable Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
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;
}

总的看下来,构造函数主要设置三个参数Looper对象,Callback对象,布尔类型async。Callback对象主要涉及消息的处理,后面说,而async表示是否设置同步屏障。通过构造函数了解到Handler的消息队列指向了Looper对象的消息队列对象。

那么如何往消息队列投递信息呢?

Handler投递信息有很多重载函数,但主要通过sendMessage函数投递一个Message对象和post函数投递一个Runable对象。

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
32
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}

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

public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();

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

可见对消息Message对象设置延迟时间和同步屏障,并将Handler对象自身设置给了Message对象的target变量,最后调用的消息队列的enqueueMessage队列将消息投递到消息队列中。

1
2
3
4
5
6
7
8
9
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

post函数只是将Runable对象封装到Message对象中的callback函数。最终还是调用sendMessagedDelayed函数的历程去处理。

从这里我们定位到了在Looperloop函数中Message对象的targetHandler对象,看看dispatchMessage函数。

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

dispatchMessage函数可以看出对消息处理的优先级。

  • 消息Message对象msg自带的Runable对象callback。也就是我们通过post函数投递的Runable对象会最先被处理。
  • 优先级排第二就是我们在创建Handler对象时,设置的全局回调Callback对象mCallback
  • 优先级最低的,也是最常用的,重载handleMessage函数,该函数默认是空实现。

MessageQueue

Handler中调用了MessageQueue对象的enqueueMessage函数,将Message对象投递到队列中。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}

synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}

if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}

msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {//队列头
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

enqueueMessage函数主要根据消息处理的时间when,插入到队列合适的位置。

再看一下next函数,如何从消息队里取消息。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
Message next() {
//当应用重启该mPtr会被置null
final long ptr = mPtr;
if (ptr == 0) {
return null;
}

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {//出现同步屏障,优先处理异步信息
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {//未到消息的处理时间,等待
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {//从队列中获取消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}

//当所有挂起消息被处理了,则返回null
if (mQuitting) {
dispose();
return null;
}

//首次运行或者没有消息了
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//没有空闲的消息
mBlocked = true;
continue;
}

if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}

//避免重复执行已处理的信息
pendingIdleHandlerCount = 0;

// 重置该时间,这样可以立即处理下一等待的信息
nextPollTimeoutMillis = 0;
}
}

next函数中,如果有同步屏障,会优先返回异步信息Looper对象,然后进行处理。如果队列中没有消息,则直接处理已被挂起的消息。

异步消息与同步消息

默认情况下,Handler对象会将mAsynchronous设置为false,会把消息Message对象的asynchronous设置为false,表示异步消息。当Message对象的asynchronousture表示同步消息。而当target==null,表示这是一个同步屏障。

这三者的作用就是为了处理一个优先级的问题,当同步屏障到来时msg.target==null,优先处理队列中的异步信息。

为什么说msg.target==null是一个同步屏障,通过Handler投递出来的信息msg.target都是不为null的。而发送一个同步屏障的代码。

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
32
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}

private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;

Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}

可见,发送一个屏障,并没有设置Handler对象给target变量。而异步信息优先权大于同步信息从何体现?

MessageQueuenext函数中有这么段代码。

1
2
3
4
5
6
7
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}

当发送一个同步屏障时,需要手动移除该屏障,否则会一直停留在异步消息的处理中,因为MessageQueuenext函数没有移除同步屏障。同步屏障的一个应用例子就在ViewRootImplescheduleTraversals函数中。

线程切换

Handler的实际应用就是UI线程与其他线程之间的切换。默认情况下,主线程会创建Looper对象,不需要我们手动创建。我们在主线程创建Handler对象时没有传入Looper对象,那默认就使用了主线程的Looper对象。在其他线程投递信息最终也是在主线程处理,达到线程切换的原理。

RxJavaGlide中网络请求,耗时操作的原理也是使用Handler切回到主线程。

也有面试官会问,能否在子线程创建Handler,答案是可以,前提是要先创建Looper对象,并且开启循环,即调用Looper.preare();Looper.loop()

HandlerThread

正常情况下,使用主线程的Looper.myLooper函数获取的Looper对象来创建的Handler一般不会有问题。

但如果在使用子线程的Looper对象呢。如下面代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class LooperThread extends Thread {
public Looper myLooper;

@Override
public void run() {
Looper.prepare();
myLooper=Looper.myLooper();
Looper.loop();
}
}

val looperThread = LooperThread()
looperThread.start()
Handler(looperThread.myLooper)
handler.sendEmptyMessage(1)

定义了一个具有Looper对象的线程,这样就可以使用LooperThread对象的Looper对象。但上面代码Handler(looperThread.myLooper)有问题,可能此时Looper对象还没初始化。因此需要采用同步机制来处理该问题,不过Android已经帮我们定义了好了HandlerThread来出来该问题了。

run函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}

getLooper函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
public Looper getLooper() {
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}
return mLooper;
}

【参考链接】

Handler的学习总结