Handler源码分析之二 异步消息的处理

5/9/2021 Android源码Handler

上一篇我们讲了Handler最基础的消息分发,知道了Handler是通过nativePollOnce()和nativeWake()来等待和唤醒的,并且通过msg.when来实现延时的,同时也留下了几个问题:

  • 1 什么是异步消息AsyncMessage。
  • 2 为什么要把Looper放在ThreadLocal里面。
  • 3 ActivityThread.main()是在哪里被调用的。 本章我们就带着问题来看一下相关源码。

# 1 异步消息AsyncMessage

我们先来看第一篇中在哪里碰到了异步消息:

Message next() {
    ...省略部分代码...
    for (; ; ) {
       ...省略部分代码...
        synchronized (this) {
            Message prevMsg = null;
            Message msg = mMessages;
            //这里循环找异步消息 msg.isAsynchronous
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            ...省略部分代码...
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

我们来看看msg.isAsynchronous代码:

public boolean isAsynchronous() {
    return (flags & FLAG_ASYNCHRONOUS) != 0;
}
static final int FLAG_ASYNCHRONOUS = 1 << 1;
1
2
3
4

那么我们来看看在哪里添加了这个flag:

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}
1
2
3
4
5
6
7

现在我们看到Message.setAsynchronous(boolean async)这个方法用来控制是否是异步消息,在哪里调用了呢, 我们先来看View.requestLayout():

public void requestLayout() {
    ...省略部分代码...
    //这里的mParent是自己的父View,如果view本身已经是最顶层view,则mParent就是ViewRootImpl
    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
    ...省略部分代码...
}
1
2
3
4
5
6
7
8

这里会层层向上调用mParent.requestLayout(),直到调用到ViewRootImpl.requestLayout()为止

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread(); //检查当前线程
        mLayoutRequested = true; //更新标记
        scheduleTraversals(); //开始遍历
    }
}
1
2
3
4
5
6
7
8
void checkThread() {
    //这里检查线程是否是mThread,不是的话就抛出异常,mThread一般是UI线程
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
    }
}
1
2
3
4
5
6
public boolean mTraversalScheduled;
int mTraversalBarrier;
Choreographer mChoreographer;
void scheduleTraversals() {
    if (!mTraversalScheduled) { //不在执行中才执行
        mTraversalScheduled = true; //更新标记为执行中
        //向mHandler的MessageQueue中添加一个同步屏障,一个同步屏障也是一个Message,但是这个Message的target是null,并且后面会有一个异步消息AsynchronousMessage
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //这里是重点!!!,通过Choreographer来post一个消息,//TAG1: CALLBACK_TRAVERSAL,要记住这个TAG
        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ...省略部分代码...
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

我们先来看MessageQueue.postSyncBarrier():

public int postSyncBarrier() {
    //这里使用当前时间作为参数传递进去
    return postSyncBarrier(SystemClock.uptimeMillis());
}

private int postSyncBarrier(long when) {
    synchronized (this) {
        final int token = mNextBarrierToken++; //token增加1
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when; //msg.when就是当前时间
        msg.arg1 = token; //arg1就是token
        //注意!!!这个msg没有target,也就是msg.target=null

        //循环遍历,将这个同步屏障msg插入到队列
        Message prev = null;
        Message p = mMessages;

        //遍历查找when之前的消息
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        //插入到队列
        if (prev != null) {
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}
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

再来看看removeSyncBarrier():

//这个token就是我们上面保存的
public void removeSyncBarrier(int token) {
    synchronized (this) {
        //遍历删除这个token对应的msg
        Message prev = null;
        Message p = mMessages;
        //只要p.target!=null || p.arg1!=token就一直找,因为上面我们分析了同步屏障的target=null,并且arg1=token
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        //检测队列
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;
        //移除同步屏障消息
        if (prev != null) {
            prev.next = p.next;
            needWake = false;
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        //回收
        p.recycleUnchecked();

        //如果需要唤醒,则唤醒等待
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}
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

然后看mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);第一个参数是个int,第二个参数mTraversalRunnable是个Runnable,代码如下:

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

void doTraversal() {
    if (mTraversalScheduled) { //不在执行中才执行
        mTraversalScheduled = false; //更新标记为执行中
        //从mHandler的MesssageQueue中移除同步屏障,还记得刚刚添加同步屏障的代码吗
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        //开始遍历布局
        performTraversals();
    }
}

//这个方法对view进行了测量,布局和绘制,我们后面会细讲
private void performTraversals() {
    ......
    performMeasure();
    ......
    performLayout();
    ......
    performDraw();
    ......
}
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

我们来看下Choreographer类的部分关键代码:

class Choreographer {
    public static final int CALLBACK_INPUT = 0;
    public static final int CALLBACK_ANIMATION = 1;
    public static final int CALLBACK_INSETS_ANIMATION = 2;
    public static final int CALLBACK_TRAVERSAL = 3; //这是刚刚的第一个参数
    public static final int CALLBACK_COMMIT = 4;
    private static final int CALLBACK_LAST = CALLBACK_COMMIT;
    private final CallbackQueue[] mCallbackQueues;
    private final FrameHandler mHandler;
    private final FrameDisplayEventReceiver mDisplayEventReceiver;

    private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;

        mHandler = new FrameHandler(looper);

        //这里初始化mDisplayEventReceiver
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
        mLastFrameTimeNanos = Long.MIN_VALUE;

        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

        //这里对mCallbackQueues进行初始化,直接new出来一个长度为5的数组,并且初始化5个元素,为什么长度是5?因为上面说了CALLBACK_LAST=4
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
    }

    //这就是刚刚调用的方法
    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0); //第四个参数是0
    }

    public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {
        if (action == null) {
            throw new IllegalArgumentException("action must not be null");
        }
        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
            throw new IllegalArgumentException("callbackType is invalid");
        }

        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
    }

    //最终调到这里
    private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis(); //获取当前时间
            final long dueTime = now + delayMillis; //delayMillis = 0,所以dueTime = now
            //这是个数组 callbackType是CALLBACK_TRAVERSAL,dueTime=0,action就是那个mTraversalRunnable
            //这里就是将这个mTraversalRunnable保存在CALLBACK_TRAVERSAL对应的集合里了
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) { //true
                scheduleFrameLocked(now); //跑这里
            } else {
                //否则通过Handler发送出去
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

    //接着看
    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
                //看这里
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); //msg.what = MSG_DO_SCHEDULE_VSYNC
                    msg.setAsynchronous(true); //标记为异步消息!! 前面我们加了个同步屏障了的
                    mHandler.sendMessageAtFrontOfQueue(msg); //发送,这个mHandler是FrameHandler
                }
            } else {
                final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }
}
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

接着来看FrameHandler的代码:

private final class FrameHandler extends Handler {
    public FrameHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_DO_FRAME:
                doFrame(System.nanoTime(), 0);
                break;
            case MSG_DO_SCHEDULE_VSYNC: //跑到了这里
                doScheduleVsync();
                break;
            case MSG_DO_SCHEDULE_CALLBACK:
                doScheduleCallback(msg.arg1);
                break;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

接着看:

void doScheduleVsync() {
    synchronized (mLock) {
        if (mFrameScheduled) {
            scheduleVsyncLocked();
        }
    }
}

private void scheduleVsyncLocked() {
    mDisplayEventReceiver.scheduleVsync();
}

//来看下scheduleVsync方法:
public void scheduleVsync() {
    if (mReceiverPtr == 0) {
        Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                + "receiver has already been disposed.");
    } else {
        //这里直接调用了native的方法,native调用完成会回到
        nativeScheduleVsync(mReceiverPtr);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

native完事后会回调到FrameDisplayEventReceiver.onVsync()里面:

private final class FrameDisplayEventReceiver extends DisplayEventReceiverimplements Runnable {
    private boolean mHavePendingVsync;
    private long mTimestampNanos;
    private int mFrame;

    public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
        super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
    }

    @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
    long now = System.nanoTime();
    if (timestampNanos > now) {
        timestampNanos = now;
    }

    if (mHavePendingVsync) {
        Log.w(TAG, "Already have a pending vsync event.  There should only be "
                + "one at a time.");
    } else {
        mHavePendingVsync = true;
    }

    mTimestampNanos = timestampNanos;
    mFrame = frame;
    Message msg = Message.obtain(mHandler, this); //这里的第二个参数是this,意味着msg.callback = this,那么就会跑到自己的run函数 
    msg.setAsynchronous(true);
    mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}

    @Override
    public void run() {
        //跑到这里了
        mHavePendingVsync = false;
        doFrame(mTimestampNanos, mFrame);
    }
}
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
void doFrame(long frameTimeNanos, int frame) {
    final long startNanos;
    synchronized (mLock) {
        if (!mFrameScheduled) {
            return; // no work to do
        }
    ...省略计算时间的代码...
    //下面开始执行事件顺序为: 1 输入事件 2 动画 3 布局
    try {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
        AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

        mFrameInfo.markInputHandlingStart();
        doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

        mFrameInfo.markAnimationsStart();
        doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
        doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

        mFrameInfo.markPerformTraversalsStart();
        //TAG: CALLBACK_TRAVERSAL,还记得这个TAG吗,我们看这里即可
        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

        doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
    } finally {
        AnimationUtils.unlockAnimationClock();
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

void doCallbacks(int callbackType, long frameTimeNanos) {
    CallbackRecord callbacks;
    synchronized (mLock) {
        //这里根据callbackType取出所有事件,这里的type是:CALLBACK_TRAVERSAL
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);
        if (callbacks == null) {
            return;
        }
        mCallbacksRunning = true;
        ...省略部分代码...
    }
    try {
        //遍历取出所有事件,执行,还记得我们之前的那个:mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)吗?
        //第二个参数就是i一个i饿runnable,就会被执行,也就是执行到我们的traversal()里去了,最终就会去测量、布局、绘制
        for (CallbackRecord c = callbacks; c != null; c = c.next) {
            c.run(frameTimeNanos);
        }
    } finally {
        ...回收callback...
    }
}
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

通过以上分析我们了解到: Android在布局的时候使用了异步消息,为什么要使用异步消息呢,有什么好处,答案就是:异步消息会被优先处理,比如当前MessageQueue中有100个消息,这时候有个布局消息来了,正常的话要等待100个执行完,如果是异步消息,则可以优先执行,这样UI就不会因为消息过多而卡顿,这就是优点,我们再来看看MessageQueue.next的代码:

Message next() {
    ...省略部分代码...
    for (;;) {
        ...省略部分代码...
        synchronized (this) {
            final long now = SystemClock.uptimeMillis(); //记录当前时间
            Message prevMsg = null;
            Message msg = mMessages; //取出第一条消息,也就是队头消息

            //msg.target == null! 上面分析过,这是一个同步屏障,而同步屏障后面一般会有一个人异步消息,同步屏障的目的就是为了标记后面有个异步消息的
            if (msg != null && msg.target == null) { //如果有同步屏障,那么就找异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous()); //果然在这里,只要不是异步消息,就一直找,直到找到为止
            }

            //开始处理消息
            if (msg != null) {
                ...处理msg...
            } else {
                // 标记为-1无限等待
                nextPollTimeoutMillis = -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

上面省略了部分代码,主要分析了 异步消息是被优先执行的,而且异步消息都跟在同步屏障后面的。

# 2 为什么要把Looper放在ThreadLocal里面

我们先来看怎么把Looper放在ThreadLocal里面的?

Looper.prepare()

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
1
2
3
4
5
6
7

ThreadLocal.set()

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
1
2
3
4
5
6
7
8

ThreadLocalMap.getMap() 可以看到Thread有一个成变量ThreadLocalMap

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
public class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;

}
1
2
3
4
5
6
7

createMap() 这里直接创建一个ThreadLocalMap

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
1
2
3

这里来总结一下存放Looper的逻辑

  • 1 每个线程都有个ThreadLocalMap的成员变量threadLocals,这个变量可以理解为一个HashMap,其中key是ThreadLocal,value是任意类型
  • 2 Looper内部有个ThreadLocal成员变量sThreadLocal,在prepare()的时候,会先创建一个Looper,然后获取当前线程的ThreadLocalMap,并且把自自己的成员变量sThreadLocal作为key,把这个looper作为值存放进去,可以简单理解为:
Looper looler = new Looper(); //创建一个looper
ThreadLocalMap map = Thread.currentThread().getMap(); //获取当前线程的ThreadLocalMap
map.set(sThreadLocal, looper); //将looper存入当前线程的ThreadLocalMap中
1
2
3

我们再来看看是怎么从ThreadLocal里面获取Looper的?

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
1
2
3

很简单,直接使用ThreadLocal的get()函数,这里可以缕一下: 因为ThreadLocalMap是每个线程都有的成员变量,所以在线程A里面使用Looper.myLooper()得到的就是线程A的looper,线程B得到的就是线程B的looper,UI线程肯定就是UI线程的looper,所以就达到了线程切换的目的。

# 3 ActivityThread.main()是在哪里被调用的

答: 是在app进程被创建的时候调用的,app进程被创建的时候,会通过zygote来fork一个新进程作为app进程,然后通过反射调用ActivityThread的main方法,详细流程后面我们专门出一个章节来讲。

# 总结

1 异步消息跟在同步屏障后面(不一定是紧临),异步消息会被优先执行,寻找异步消息的过程是先找同步屏障,如果有,就遍历next来找异步消息,同步屏障的特点是msg.target=null,异步消息的特点是isAsynchronous = true。

eg: 同步屏障是50,异步消息是100,同步屏障前面的1-49会先执行,然后执行到50的时候发现是同步屏障,然后就遍历后续消息,找到异步消息100,取出来执行,然后移除同步屏障50,在继续执行后面的消息51,以此类推。

2 使用ThreadLocal是为了将Looper保存在线程内部,从而达到跨线程的目的。

Last Updated: 3/3/2022, 10:09:37 AM