0%

⎡稳扎稳打系列⎦:Handler机制全面解析3(3/3)

2022-02-18-ekaterina-sazonova-CMhxw3lgZ7M-unsplash-modified.webp

1. 延时消息的原理

无论通过Handler的哪个post函数发消息,最终都会来到sendMessageAtTime,基于android.os.SystemClock#uptimeMillis时间,最终以SystemClock.uptimeMillis()+delayMillis的结果作为入队列和消息执行的基准。

1.1 延时消息是怎样入队列的?

消息入队列是通过MessageQueue#enqueueMessage函数,when表示消息执行的时间,通过下方源码可知如果当前消息队列为空,或者when等于0,或者新消息的when小于头消息的when,则直接将新消息替换为头消息。否则按照when的值将消息插入到队列的合适位置中。

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
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 当前消息队列为空 or when等于0 or 新消息的when小于头消息的when
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
// 否则按照when的值将消息插入到队列的合适位置中
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
...
}
return true;
}

1.2 延时消息是怎样被唤醒并执行的?

其实这个问题在上一节中已经分析过了,在MessageQueue#next中会调用nativePollOnce在native层轮询一次,这里可能会导致阻塞,最终会进入native层Looper.cpp中的pollInner函数,调用epoll_wait等待感兴趣的事件或超时发生,而这里会有个timeoutMillis描述需要等待的时间。

有关epoll_wait更详细的分析,参考2021-12-16-⎡稳扎稳打系列⎦:Handler机制全面解析2/#3-向消息队列取消息

2. IdleHandler的作用和原理

IdleHandler是定义在MessageQueue中的静态内部接口,源码如下:

frameworks/base/core/java/android/os/MessageQueue.java

1
2
3
public static interface IdleHandler {
boolean queueIdle();
}

IdleHandler终归也是一种Handler,也是需要执行的,只是它的执行时机有点特殊而已。

a. 消息队列为空
b. 消息队列中的消息还没到执行时间

通过其执行时机,可以知道IdleHandler的作用就是在消息队列空闲的时候处理一些非紧急任务或者优先级不那么高的任务。

frameworks/base/core/java/android/os/MessageQueue.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private IdleHandler[] mPendingIdleHandlers;

public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}

public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}

外部通过MessageQueue#addIdleHandler添加IdleHandler,在next函数中会将IdleHandler拷贝到临时数组mPendingIdleHandlers中。然后在next函数空闲时遍历mPendingIdleHandlers并逐一回调queueIdle执行。

frameworks/base/core/java/android/os/MessageQueue.java

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
Message next() {
...

// 如果空闲会走到下面代码

if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}

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

// 遍历
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i]; // 取出IdleHandler实例
mPendingIdleHandlers[i] = null; // 每次都清空临时数组中的实例

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

if (!keep) {
synchronized (this) {
// 如果不需要保留IdleHandler实例,则移除mIdleHandlers中的实例
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}

总结下IdleHandler流程

a. 本次轮询中如果msg为空,或者msg还没到触发时间,则判定为空闲状态
b. 接着遍历mPendingIdleHandlers数组取出IdleHandler实例并调用queueIdle方法
c. 如果queueIdle返回false表示执行完后不需要保留,则在mIdleHandlers中直接彻底移除

处理完IdleHandler后会将nextPollTimeoutMillis置为0,也就是不阻塞消息队列,这里要注意queueIdle不能太耗时,因为它是同步执行的,如果太耗时肯定会影响后面的message执行。

3. Android消息通信机制中IPC方式的变迁

Android所拥有的IPC总共有这些:

a. 基于Unix系统的IPC的管道,FIFO,信号
b. 基于SystemV和Posix系统的IPC的消息队列,信号量,共享内存
c. 基于Socket的IPC
d. Linux的内存映射函数mmap()
e. Linux 2.6.22版本后才有的eventfd
f. Android系统独有的Binder和匿名共享内存Ashmen

这里要讨论的是消息通信机制中的IPC变迁,其实也就是Looper唤醒方式的变迁,大体上是从【a. 基于Unix系统的IPC的管道,FIFO,信号】到【e. Linux 2.6.22版本后才有的eventfd】的升级。

3.1 管道

PIPE和FIFO都是指管道,只是PIPE独指匿名管道,FIFO独指有名管道。有关管道的具体分析参考深入理解Android进程间通信机制-管道

pipe和fifo的异同点

相同 不同
IPC的本质都是通过在内核创建虚拟文件,并且调用文件读写函数来进行数据通信 pipe是单向通信,fifo可以双向通信
都只能接收字节流数据 pipe只能在父子,兄弟进程间通信,fifo没有这个限制
都是半双工通信 pipe是单向通信,不存在并发问题;fifo可以双向通信,这样不可避免的带来了并发的问题

在Android6.0以下版本中,主线程Looper的唤醒就使用到了管道。

android-5.0.1_r1分支,system/core/libutils/Looper.cpp

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
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
int wakeFds[2];
int result = pipe(wakeFds); // 创建pipe
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);

mWakeReadPipeFd = wakeFds[0]; // 写文件描述符
mWakeWritePipeFd = wakeFds[1]; // 读文件描述符

result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);

result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);

mIdling = false;

// Allocate the epoll instance and register the wake pipe.
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);

struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
}

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif

ssize_t nWrite;
do {
// 往管道mWakeWritePipeFd里写入一个字母“W”
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);

if (nWrite != 1) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}

可以看到,唤醒函数其实就是往管道mWakeWritePipeFd里写入一个字母“W”,mWakeReadPipeFd接收到数据后,就会唤醒Looper。

3.2 eventfd

eventfd是Linux 2.6.22后才开始支持的一种IPC通信方式,它的作用主要是用来做事件通知,并且完全可以替代pipe,对于内核来说,eventfd的开销更低,eventfd只需要创建一个虚拟文件,而pipe需要创建两个,并且可用于select或epoll等多路复用模型中,来实现异步的信号通知功能。所以eventfd是很好用的一种IPC方式,而且它的使用也简单。

eventfd在内核里的核心是一个计数器counter,它是一个uint64_t的整形变量counter,初始值为initval。

当调用read() 函数读取eventfd时,会根据counter值执行下列操作:

a. 如果当前counter > 0,那么read返回counter值,并重置counter为0
b. 如果当前counter等于0,那么read 函数阻塞直到counter大于0,如果设置了NONBLOCK,那么返回-1

当调用write() 往eventfd写数据时,我们只能写入一个64bit的整数value。

正是因为eventfd比管道更简单高效,所以在Android6.0之后,Looper的唤醒就换成了eventfd。

android-6.0.0_r1分支,system/core/libutils/Looper.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK); // 创建eventfd
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd. errno=%d", errno);

AutoMutex _l(mLock);
rebuildEpollLocked();
}

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif

uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t))); // 写入数字1
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}

可以看到,Looper的构造函数中mWakeEventFd已经由之前提到的pipe换成了evnentfd,wake()函数也不是之前的写入一个“w”字符,而是写入了一个64位整数1。

从pipe到eventfd可以看出,即使在Android源码级别,也是在不断更迭代码,往更简单更高效的方向在努力和优化。

4. 屏幕刷新机制与消息机制的结合运作

关于Android屏幕刷新机制原理,强烈推荐这篇:“终于懂了” 系列:Android屏幕刷新机制—VSync、Choreographer 全面理解!

在这一系列文章中,屏幕刷新机制感兴趣的看上面的文章,这里我们重点关注屏幕刷新绘制与消息机制是怎么结合运作的

4.1 绘制入口

所有UI的变化最终都是走到ViewRootImpl的scheduleTraversals()方法,那么scheduleTraversals()到performTraversals()中间经历了什么呢?是立刻执行吗?答案很显然是否定的,只有在VSync信号到来时才会执行绘制,即performTraversals()方法。

下面就从源码角度分析这是如何实现的:

frameworks/base/core/java/android/view/ViewRootImpl.java

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
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 发送消息屏障,屏蔽同步消息,从而保证VSync到来立即执行绘制
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// TraversalRunnable是内部类,会走run()方法,最终执行doTraversal()方法
// 注意这里的callback类型是CALLBACK_TRAVERSAL
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

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

void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}

// 开始三大绘制流程
performTraversals();

if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}

总结下scheduleTraversals()方法到performTraversals()方法的流程:

a. 发送消息屏障,屏蔽同步消息,从而保证VSYNC信号到来时可被立即执行
b. mChoreographer.postCallback()方法投递mTraversalRunnable,其消息类型是CALLBACK_TRAVERSAL
c. 执行mTraversalRunnable的run()方法,移除屏障,并执行performTraversals()方法

上面只是大致流程,这里还有一些疑问点:

a. VSYNC信号是怎么到来的,是怎样跟消息机制结合的?
b. mChoreographer.postCallback()发送的CALLBACK_TRAVERSAL代表什么含义?
c. mTraversalRunnable的run()方法是怎么被调起执行的?

带着这些问题继续看下面的分析。

4.2 Choreographer与FrameHandler结合

在ViewRootImpl的构造函数中会获取Choreographer实例,在Choreographer的构造函数中会创建FrameHandler实例,所以Choreographer与FrameHandler结合在于其构造函数,同时利用FrameHandler将VSYNC信号到来时的消息同步到主线程中。

frameworks/base/core/java/android/view/Choreographer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper); // 创建FrameHandler实例
//USE_VSYNC 4.1以上默认是true,表示具备接受VSync的能力,这个接受能力就是FrameDisplayEventReceiver
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource) // vsync信号接收器
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;

// 计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

// 创建一个链表类型CallbackQueue的数组,大小为5,
//也就是数组中有五个链表,每个链表存相同类型的任务:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}

Choreographer中的CALLBACK类型总共是5类,分别是:输入、动画、插入更新动画、布局和绘制、提交

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
/**
* Callback type: Input callback. Runs first.
* @hide
*/
public static final int CALLBACK_INPUT = 0;

/**
* Callback type: Animation callback. Runs before {@link #CALLBACK_INSETS_ANIMATION}.
* @hide
*/
@TestApi
public static final int CALLBACK_ANIMATION = 1;

/**
* Callback type: Animation callback to handle inset updates. This is separate from
* {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
* {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} for multiple
* ongoing animations but then update the whole view system with a single callback to
* {@link View#dispatchWindowInsetsAnimationProgress} that contains all the combined updated
* insets.
* <p>
* Both input and animation may change insets, so we need to run this after these callbacks, but
* before traversals.
* <p>
* Runs before traversals.
* @hide
*/
public static final int CALLBACK_INSETS_ANIMATION = 2;

/**
* Callback type: Traversal callback. Handles layout and draw. Runs
* after all other asynchronous messages have been handled.
* @hide
*/
public static final int CALLBACK_TRAVERSAL = 3;

/**
* Callback type: Commit callback. Handles post-draw operations for the frame.
* Runs after traversal completes. The {@link #getFrameTime() frame time} reported
* during this callback may be updated to reflect delays that occurred while
* traversals were in progress in case heavy layout operations caused some frames
* to be skipped. The frame time reported during this callback provides a better
* estimate of the start time of the frame in which animations (and other updates
* to the view hierarchy state) actually took effect.
* @hide
*/
public static final int CALLBACK_COMMIT = 4;

private static final int CALLBACK_LAST = CALLBACK_COMMIT;

五种类型任务存入对应的CallbackQueue中,每当收到VSYNC信号时,Choreographer将首先处理INPUT类型的任务,然后是ANIMATION类型,最后才是TRAVERSAL类型。

现在回头看mChoreographer.postCallback(),最终会进入到scheduleVsyncLocked()方法中,通过mDisplayEventReceiver.scheduleVsync()申请vsync信号。

frameworks/base/core/java/android/view/Choreographer.java

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
96
97
98
99
100
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
synchronized (mLock) {
// 当前时间
final long now = SystemClock.uptimeMillis();
// 延迟时间
final long dueTime = now + delayMillis;
// 取对应类型的CallbackQueue添加任务
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

if (dueTime <= now) {
// 立即执行
scheduleFrameLocked(now);
} else {
// 延迟运行,最终也会走到scheduleFrameLocked()
// mHandler就是FrameHandler实例
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true); // 设置成异步
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}

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, new DisplayEventReceiver.VsyncEventData());
break;
case MSG_DO_SCHEDULE_VSYNC:
// 申请VSYNC信号,例如当前需要绘制任务时
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
// 需要延迟的任务,最终还是执行上述两个事件
doScheduleCallback(msg.arg1);
break;
}
}
}

void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}

private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
// 开启了VSYNC
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}

// 当前执行的线程,是否是mLooper所在线程
if (isRunningOnLooperThreadLocked()) {
// 申请VSYNC信号
scheduleVsyncLocked();
} else {
// 若不在,就用mHandler发送消息到原线程,最后还是调用scheduleVsyncLocked方法
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true); // 异步消息
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
// 如果未开启VSYNC则直接doFrame方法(4.1后默认开启)
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}

private void scheduleVsyncLocked() {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#scheduleVsyncLocked");
// vsync信号接收器
mDisplayEventReceiver.scheduleVsync();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}

FrameHandler的作用很明显里了

a. 发送异步消息(因为前面设置了同步屏障)
b. 有延迟的任务发延迟消息
c. 不在原线程的切换到原线程
d. 没开启VSYNC的直接走doFrame方法取执行绘制

总结下Choreographer与FrameHandler结合这一小节

a. 如果系统未开启VSYNC机制,此时直接发送MSG_DO_FRAME消息到FrameHandler执行doFrame()方法
b. Android4.1之后系统默认开启VSYNC,最终通过scheduleVsyncLocked()方法申请VSYNC信号
c. 如果当前不在原线程,通过FrameHandler切换到主线程,最终还是调用scheduleVsyncLocked()方法申请VSYNC信号

4.3 申请vsync信号后的执行流程

通过scheduleVsyncLocked()方法申请VSYNC信号最终会来到DisplayEventReceiver中的scheduleVsync()方法,内部会调用 nativeScheduleVsync()方法,在DisplayEventReceiver的构造函数中会注册VSYNC信号监听者mReceiverPtr,当VSYNC信号到来时,会回调onVsync()方法。下面整理出源码的调用路径:

frameworks/base/core/java/android/view/DisplayEventReceiver.java

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

private static native void nativeScheduleVsync(long receiverPtr);

// 构造函数
public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) {
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}

mMessageQueue = looper.getQueue();
// 注册VSYNC信号监听者
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource, eventRegistration);
}

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 {
// 申请VSYNC中断信号,会回调onVsync方法
nativeScheduleVsync(mReceiverPtr); // mReceiverPtr是VSYNC信号监听者
}
}

public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
}

onVsync的实现在FrameDisplayEventReceiver类中,最终会执行doFrame()方法

frameworks/base/core/java/android/view/Choreographer.java

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"Choreographer#onVsync " + vsyncEventData.id);
}
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
// earlier than the frame time, then the vsync event will be processed immediately.
// Otherwise, messages that predate the vsync event will be handled first.
long now = System.nanoTime();
if (timestampNanos > now) {
Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
+ " ms in the future! Check that graphics HAL is generating vsync "
+ "timestamps using the correct timebase.");
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;
mLastVsyncEventData = vsyncEventData;
// 将本身作为runnable传入msg, 发消息后等待执行
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true); // 异步消息
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}

// 当MessageQueue轮询到该消息后,会走run()方法,最终会调用doFrame()方法
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}

void doFrame(long frameTimeNanos, int frame, DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
final long frameIntervalNanos = vsyncEventData.frameInterval;
try {
synchronized (mLock) {
...

if (!mFrameScheduled) {
traceMessage("Frame not scheduled");
return; // no work to do
}

...

// 预计执行时间
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
// 超时时间是否超过一帧
// 这是因为MessageQueue虽然添加了同步屏障,但还是有正在执行的同步任务,导致doFrame()延迟执行了
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= frameIntervalNanos) {
// 计算掉帧数
final long skippedFrames = jitterNanos / frameIntervalNanos;
// 掉帧数超过30,打印日志
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % frameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (frameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
frameTimeNanos = startNanos - lastFrameOffset;
}

if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
traceMessage("Frame time goes backward");
scheduleVsyncLocked();
return;
}

if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
traceMessage("Frame skipped due to FPSDivisor");
scheduleVsyncLocked();
return;
}
}

mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
// Frame标志位恢复
mFrameScheduled = false;
// 记录最后一帧时间
mLastFrameTimeNanos = frameTimeNanos;
mLastFrameIntervalNanos = frameIntervalNanos;
mLastVsyncEventData = vsyncEventData;
}

// 按类型顺序执行任务
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

mFrameInfo.markInputHandlingStart();
// 输入类型
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);

mFrameInfo.markAnimationsStart();
// 动画类型
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
// 动画更新类型
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
frameIntervalNanos);

mFrameInfo.markPerformTraversalsStart();
// 布局和绘制
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);

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

void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
// 根据指定类型查找到达执行时间的CallbackRecord
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS;
if (callbacks == null) {
return;
}
mCallbacksRunning = true;

// 提交任务类型
if (callbackType == Choreographer.CALLBACK_COMMIT) {
final long jitterNanos = now - frameTimeNanos;
if (jitterNanos >= 2 * frameIntervalNanos) {
final long lastFrameOffset = jitterNanos % frameIntervalNanos + frameIntervalNanos;
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
}
}
}
try {
// 遍历执行所有任务
for (CallbackRecord c = callbacks; c != null; c = c.next) {
// 回调run()方法,内部回调Callback类型的run()
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
// 回收CallbackRecord
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}

private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
// 通过postFrameCallback或postFrameCallbackDelayed会执行这里
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
// 执行Runnable的run()
((Runnable)action).run();
}
}
}

前面看到mChoreographer.postCallback传的token是null,所以取出action就是Runnable,执行run(),这里的action就是 ViewRootImpl发起的绘制任务mTraversalRunnable了,那么这样整个逻辑就闭环了。

下面整理了完整的流程图:

2022-02-18-屏幕刷新机制与消息机制的结合运作.png

5. 总结

Handler机制全面解析系列共3篇终于完成了。

2021-12-01-⎡稳扎稳打系列⎦:Handler机制全面解析1.md
2021-12-16-⎡稳扎稳打系列⎦:Handler机制全面解析2.md
2022-02-18-⎡稳扎稳打系列⎦:Handler机制全面解析3.md

系列开篇分析了Handler的运行机制,ActivityThread、Handler、Looper、MessageQueue之间关系,Message的类型,消息分发回调的优先级是怎样的以及Handler的内存泄漏。这些都是Handler机制的核心基础知识,比如Message的类型这一点,之前自己是不清楚的,导致对于全面理解机制原理是很困难的,系统怎么保证某类消息优先执行,为什么业务层传的Message其target不能为空等问题其实都跟Message的类型有关。

打通知识连贯性是很重要的,知识点不应该是一个一个单独的,而是相互关连相互牵扯的。再比如消息分发回调的优先级,在某些插件化方案中,这里Callback作为Hook点被使用,所以消息机制跟插件化有了关连关系。试想如果对消息机制不清楚的话,插件话开发找hook点怎么可能想到这里?

在第二篇中主要是分析了“存消息”和“取消息”的源码和原理,这里涉及到Java和Native两个世界的协同工作,而MessageQueue就是它们协作的桥梁。在看源码过程中,看到Looper取消息是一个死循环,进而分析了死循环的意义。“存消息”和“取消息”的过程涉及唤醒和等待,这里就牵扯出了epoll机制以及IPC机制。在Android6.0以下版本中,主线程Looper的唤醒就使用到了管道。到了Android6.0之后,Looper的唤醒就换成了eventfd。这便是IPC机制的变迁和升级。

在最后一篇中,先是分析了两个小问题:延时消息的原理以及IdleHandler的作用和原理,这两块算是自己的知识盲区,这里疏通了。最后其实我一直有个疑问就是:屏幕刷新绘制到底是怎么被通知和执行的呢?都知道绘制是从performTraversals()开始进行三大绘制流程,那么它本身是如何被调起执行的?VSYNC信号是怎么产生并给到主线程的?主线程收到信号后是怎么组织代码进行一系列处理的?这些问题在最后一篇的最后一节进行了全过程分析。这一部分也有全流程图供查阅源码时参考。

写完三篇文章后,自己对Handler机制的知识点有了大致的把握,后续如有需要再更新和完善吧。


参考