1.Handler的诞生

由于Android采用的是单线程模式,开发者无法在子线程中更新 UI,因此在Android开发中,经常会在子线程中进行一些操作,当操作完成之后,将结果发送到主线程进行显示。探索其背后的模式:子线程、Handler和主线程三者组成了生产者和消费者模式。子线程负责生产数据,主线程负责消费数据,而Handler负责将数据从子线程抛到主线程中。详细见下图

Handler生产者消费者模型.png

2.Handler相关的类

  • Hanlder:发送和接收消息
  • Looper:用于轮询消息队列,一个线程只能有一个Looper
  • Message: 消息实体
  • MessageQueue: 消息队列用于存储消息和管理消息。

2.1 Looper的创建

创建Looper的方法是调用Looper.prepare() 方法或者Looper.prepareMainLooper()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// threadlocal保证一个线程只有一个looper
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //如果prepare()被调用两次就会抛异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//但Thread的map中没有looper,就创建一个
}

@Deprecated //该方法被废弃的原因是:Android帮我们调用了,自己不能调用
public static void prepareMainLooper() {
prepare(false); // 调到上述方法,创建looper
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

2.2 创建MessageQueue以及与Looper和当前线程绑定

1
2
3
4
5
6
7
8
9
10
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);// 创建MessageQueue
mThread = Thread.currentThread();//与当前线程绑定
}

// quitAllowed参数子线程传入的true,主线程传入false,代表主线程不允许销毁队列
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}

2.3 Looper.loop()方法

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
public static void loop() {
final Looper me = myLooper(); //从threadlocal中拿到调prepare()创建的looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
......

me.mInLoop = true;
final MessageQueue queue = me.mQueue;

......

for (;;) {
//不断从消息队列取消息。
//queue.next()取消息的过程利用了epoll机制,当没有消息的时候阻塞,当有消息时,向管道写入一个字节数据,唤醒线程取消息。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;//如果队列返回null,说明quit()被调用,需要退出线程
}

......
try {
// 回调抛message的Handler的dispatchMessage,最终会调到handler接收到的callback.handleMessage(msg)中或者handler重写的handleMessage(msg)中。
msg.target.dispatchMessage(msg);
......
//回收处理完的消息(Message采用了享元模式,防止不停的new Message导致内存抖动)
msg.recycleUnchecked();
}
}

2.4 Handler的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//方式1
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//处理自己抛的消息
}
};
//方式2 传入main looper或者子线程looper,通常传MainLooper
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
//方式3 传入looper和callback,消息处理在callback中
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}

2.5 Message的创建

可以直接new Message()但是不建议这么使用,因为如果有大量的new Message()然后用完了就被回收,这样会导致内存抖动。推荐的方式是使用obtain()系列方法。Message使用了享元模式,内部维护了一个对象池(最大50个),管理者对象的创建和销毁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next; //从对象池头部取一个message
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
//Message中有一系列obtain()方法,主要是完成handler绑定或者callback绑定工作,就不全列举了
public static Message obtain(Handler h) //Message和handler绑定
public static Message obtain(Handler h, Runnable callback)//Message与handler和Runnable绑定
public static Message obtain(Handler h, int what) //Message与handler和事件码绑定

2.6 Message和Handhler绑定

  • 方式1:通过上面创建Message时绑定

  • 方式2:在发送消息的到时候绑定(代码如下)

1
2
3
4
5
6
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this; //绑定handler
......
return queue.enqueueMessage(msg, uptimeMillis);
}

2.7 Handler发送消息

如下图调用关系,Handler发送消息的重载方法很多,但是主要只有2种。 sendMessage(Message) sendMessage方法通过一系列重载方法的调用,sendMessage调用sendMessageDelayed,继续调用sendMessageAtTime,继续调用sendMessageAtTime,继续调用enqueueMessage,继续调用MessageQueue的enqueueMessage方法,将消息保存在消息队列中。

发送消息.png

2.8 Handler消费消息

当Looper取出,交给Handler的dispatchMessage进行处理

我们可以看到在dispatchMessage方法中,message中callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断mCallback是否为空,mCallback在Handler构造方法中初始化,在主线程通直接通过无参的构造方法new出来的为null,所以会直接执行后面的handleMessage()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
//callback在message的构造方法中初始化或者使用handler.post(Runnable)时候才不为空
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {//如果callback不为空就调用callback的run()方法
return;
}
}
//如果没有Runnable,就回调handler重写的handleMessage()方法。
//注意,不管是否设置callback都会回调handleMessage()方法
handleMessage(msg);
}
}

3.难点问题

3.1 消息加入和取出如何保证线程安全

MessageQueue在消息入队的时候和取消息的时候都会对队列加锁,保证要么入队消息,要么出队消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 消息入队
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//当入队的时候对消息队列加锁
synchronized (this) {
......
}
// 消息出队
Message next() {
.....
for (;;) {
//循环等待消息
......
//消息出队过程加锁
synchronized (this) {
......
}

3.2 消息机制之同步屏障

通过上面分析,通常情况下Handler抛的消息会按照时间排序,然后Looper从头部开始一个一个取消息执行。但是有个需求是在一个小的时间范围内,handler抛的消息需要优先执行,那我们应该如何处理呢?例如UI需要立马重绘,而且需要优先处理这个消息,那就要用到消息同步屏障。

MessageQueue.postSyncBarrier() //开启同步屏障

MessageQueue.removeSyncBarrier() //移除同步屏障

Message.setAsynchronous(true) //设置为异步消息

3.2.1 开启同步屏障
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
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
//注意,方法调用返回一个屏障的token,到时候删除的时候会用到这个token
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token
synchronized (this) {
final int token = mNextBarrierToken++;
//从消息池中获取Message
final Message msg = Message.obtain();
msg.markInUse();

//就是这里!!!初始化Message对象的时候,并没有给target赋值,因此 target==null
msg.when = when;
msg.arg1 = token;

Message prev = null;
Message p = mMessages;

if (when != 0) {
while (p != null && p.when <= when) {
//如果开启同步屏障的时间(假设记为T)T不为0,且当前的同步消息里有时间小于T,则prev也不为null
prev = p;
p = p.next;
}
}
//根据prev是不是为null,将 msg 按照时间顺序插入到 消息队列(链表)的合适位置
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}

此处开启同步屏障之后,从消息池获取的Message中target==null。

3.2.2 处理异步消息

从下面可以看出,当消息队列开启同步屏障的时候(即标识为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
//MessageQueue.java

Message next() {
.....//省略一些代码
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//获取系统开机到现在的时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages; //当前链表的头结点

//关键!!!
//如果target==null,那么它就是屏障,需要循环遍历,一直往后找到第一个异步的消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next; //=====重新给msg赋值,达到优先返回异步消息的作用===
} while (msg != null && !msg.isAsynchronous());//如果有异步消息
}
if (msg != null) {
//如果有时间到的消息就返回,否则就等待。
}
.....//省略

}

如果开启了同步屏障,在返回的msg中会被重新赋值,所以如果开启了同步屏障,MessageQueue会遍历队列中是否有时间到了的异步消息,如果有就重新给msg赋值让他返回出去给handler处理。

示意图如下:

同步屏障示意图.png

3.2.3 同步屏障的使用场景

在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用ViewRootImpl#scheduleTraversals(),如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//开启同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//发送异步消息

// Choreographer.postCallback最终会调用到postCallbackDelayedInternal(),在这里面设置为异步消息并且发送到消息队列中
// mTraversalRunnable中会移除同步屏障并且执行UI更新
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}

synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
//将消息设置成异步消息并且发送到消息队列中,消息队列会根据target==null时取出异步消息执行
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}

3.3 同步执行消息

3.3.1 runWithScissors()分析

我们从前面的分析可以看出生产者-消费者模式其实是异步的执行操作。生产者生产好了商品向容器里面放,消费者只管从容器取就行,但是有这么一个场景,就是我生产的东西需要消费了才能继续生产下去,那么就需要考虑同步执行消息了。(可能举例不恰当)但是在framework中,WindowManagerService初始化的时候,就会出现这样的情况,WindowManagerService的初始化是放在显示子线程完成的,但是使用多线程来初始化服务虽然加快了初始化速度,但是也会出现一种情况就是某个服务没有初始化结束,导致其他服务运行异常的情况。因此系统提供了Handler.runWithScissors()这个方法来实现同步执行消息。如果任务没有执行结束,该线程不能执行其他的任务。下面我们来分析一下这个方法:

1
2
3
4
5
6
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
......
// 创建一个带阻塞功能的Runnable,主要是传入待执行的任务
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout); // 发送任务到消息队列,并且阻塞线程
}
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
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) { //发送任务到消息队列
return false;
}

synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay); //有超时时间的阻塞线程
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();//阻塞线程
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
// BlockingRunnable的run方法
public void run() {
try {
mTask.run(); //调用传入任务的run方法执行任务
} finally {
synchronized (this) {
mDone = true;
notifyAll(); //通知其他线程不再休眠
}
}
}
3.3.2 runWithScissors()使用场景
1
2
3
4
5
6
7
8
public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
//使用显示子线程来初始化WindowManagerService
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
return sInstance;
}

参考文章:

同步屏障