public final class Choreographer {
// 输入任务
public static final int CALLBACK_INPUT = 0;
// 动画任务
public static final int CALLBACK_ANIMATION = 1;
// view树遍历任务
public static final int CALLBACK_TRAVERSAL = 2;
// COMMIT任务
public static final int CALLBACK_COMMIT = 3;
// 暂存任务的链式数组
private final CallbackQueue[] mCallbackQueues;
// 主线程消息处理器
private final FrameHandler mHandler;
// 抛绘制任务
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
// 延迟抛绘制任务
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
…
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;
// 1. 将绘制任务根据类型暂存在链式结构中
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
// 2. 订阅下一个垂直同步信号
if (dueTime <= now) {
// 立刻订阅下一个垂直同步信号
scheduleFrameLocked(now);
} else {
// 在未来的某个时间点订阅垂直同步信号
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 {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
…
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);
}
}
}
}
}
Choreographer
接收到新的绘制任务后,会执行两个动作:
- 绘制任务入链:
public final class Choreographer {
// 绘制任务链
private final class CallbackQueue {
// 任务链头结点
private CallbackRecord mHead;
// 绘制任务入链(按时间升序)
public void addCallbackLocked(long dueTime, Object action, Object token) {
CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
CallbackRecord entry = mHead;
if (entry == null) {
mHead = callback;
return;
}
// 头插入
if (dueTime < entry.dueTime) {
callback.next = entry;
mHead = callback;
return;
}
//中间插入或尾插入
while (entry.next != null) {
if (dueTime < entry.next.dueTime) {
callback.next = entry.next;
break;
}
entry = entry.next;
}
entry.next = callback;
}
}
// 绘制任务结点
private static final class CallbackRecord {
// 下一个绘制任务
public CallbackRecord next;
// 绘制任务应该在这个时刻被执行
public long dueTime;
// 描述绘制任务的代码段
public Object action;
…
// 执行绘制任务
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
}
}
Choreographer
接收有四种任务类型,分别是输入、动画、View树遍历、COMMIT。每个任务被抽象成CallbackRecord
,同类任务按时间先后顺序组成一条任务链CallbackQueue
。四条任务链存放在mCallbackQueues[]
数组结构中。
- 订阅下一个垂直同步信号
public final class Choreographer {
private void scheduleFrameLocked(long now) {
// 若已经订阅过下个垂直同步信号,则什么也不做
if (!mFrameScheduled) {
// 当下一个垂直同步信号到来时,需要执行绘制任务
mFrameScheduled = true;
if (USE_VSYNC) {
// 不管走哪个分支,最终都会调用scheduleVsyncLocked()来注册接收垂直同步信号
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
…
}
}
}
// 委托 DisplayEventReceiver 来注册垂直同步信号
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
// 绘制一帧
void doFrame(long frameTimeNanos, int frame) {
synchronized (mLock) {
// 若不需要响应这个垂直同步信号,则直接返回,该帧不绘制任何东西
if (!mFrameScheduled) {
return;
}
…
}
…
}
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
// 垂直同步信号回调
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
…
// 向主线程抛任务,绘制一帧
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
// 绘制当前帧
doFrame(mTimestampNanos, mFrame);
}
}
}
// 垂直同步信号接收器
public abstract class DisplayEventReceiver {
// 注册接收下一个垂直同步信号
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, “…”);
} else {
// 向SurfaceFlinger订阅下一个垂直同步信号
nativeScheduleVsync(mReceiverPtr);
}
}
…
}
并不是每个垂直同步信号都会被上层订阅并处理,只有当
Choreographer
订阅了下一个垂直同步信号,SurfaceFlinger
才会把信号通过onVsync()
回调给上层。
图中第一个垂直信号到来之前,上层调用了postCallback()
向Choreographer
抛绘制任务,同时订阅了下一个信号并把mFrameScheduled
置为 true,表示需要绘制下一帧。当第一个信号产生时,onVsync()
被回调,同时将doFrame()
抛到主线程执行,执行完毕后将mFrameScheduled
置为 false,因没有后续订阅动作,所以上层不会收到后续的onVsync()
回调,也不会绘制任何新东西。
ViewRootImpl
向Choreographer
抛绘制任务后,任务并没有立马执行,而是被暂存在绘制任务链中,并注册接收下一个垂直同步信号。只有当下一个信号通过onVsync()
回调后,它才会被执行:
public final class Choreographer {
// 垂直同步信号接收器
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
…
// 发送异步消息到主线程,执行当前的Runnable,即doFrame()
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
// 绘制一帧的内容
doFrame(mTimestampNanos, mFrame);
}
}
}
每当垂直同步信号回调时,都会向主线程推送一个待执行的绘制帧任务
doFrame()
。
public final class Choreographer {
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
// 如果没有订阅这一帧的垂直同步信号,则直接退出不绘制
if (!mFrameScheduled) {
return; // no work to do
}
…
try {
// 处理这一帧的输入事件
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
// 处理这一帧的动画
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
// 处理这一帧的 View 树遍历
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
// 所有绘制任务结束后执行 COMMIT 任务
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
…
}
…
}
}
绘制每一帧时,会按照“输入事件”、“动画”、“View 树遍历”、“COMMIT”这样的顺序处理任务。
处理函数doCallback()
定义如下:
public final class Choreographer {
// 暂存绘制任务的链式数组
private final CallbackQueue[] mCallbackQueues;
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
// 从指定类型的任务链中根据当前时间点取出要执行的任务
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
…
}
try {
// 执行任务链上被取出的所有任务
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
} finally {
…
}
}
// 任务实体类
private static final class CallbackRecord {
public CallbackRecord next;
public Object action;
…
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
// 执行任务
((Runnable)action).run();
}
}
}
}
追踪到这,ViewRootImpl
推送过来的 “View 树遍历” 任务总算被执行了。
其中extractDueCallbacksLocked()
是任务链CallbackQueue
的方法,用于获取待当前时间点需要被执行的所有任务:
private final class CallbackQueue {
// 任务链头结点
private CallbackRecord mHead;
// 获取当前时间节点之前的所有任务,这些任务都需要在当前帧被执行
public CallbackRecord extractDueCallbacksLocked(long now) {
CallbackRecord callbacks = mHead;
if (callbacks == null || callbacks.dueTime > now) {
return null;
}
CallbackRecord last = callbacks;
CallbackRecord next = last.next;
// 遍历任务链,以现在时间点为分割线,从链上摘取过去的任务
while (next != null) {
if (next.dueTime > now) {
last.next = null;
break;
}
last = next;
next = next.next;
}
mHead = next;
// 以链的形式返回所有过去任务
return callbacks;
}
}
绘制当前帧时,会以当前时刻为分割线,从任务链上摘取所有以前的任务,并按时间先后顺序逐个执行。
在纸上演算一遍上面的代码:
上图表示在第一个垂直同步信号还未到来时,上层已经向任务链中已添加了 3 个任务,其中前两个的执行时刻在第一个信号到来之前,而第三个任务在第一个信号后执行。
在第一个任务入链时就完成了对第一个信号的订阅,而第三个任务在第一个信号之后执行,所以它的订阅行为doScheduleCallback()
先被存放在主线程消息队列中,待第一个信号到来之后再订阅第二个信号。
当第一个垂直同步信号到来后,doFrame()
被抛到主线程,它从任务链中摘取当前时间节点之前的任务1和2并执行它们。当任务被执行完,就从主线程消息队列中取出下一条消息“为任务3订阅下一个垂直同步信号”并执行。所有这些都完成后,主线程只能发呆,等待下一个垂直同步信号。
当第二个垂直同步信号到来之后,任务链中剩下的任务3都被取出并抛到主线程执行。任务链空了,当任务3执行完毕后,主线程彻底没事做,只能等到上层再向Choreographer
抛任务。
推迟
上述情况,绘制任务都能在一个帧间隔内完成,若任务很耗时,执行时间超过了帧间隔会怎么样?
第一个垂直信号到来后,任务1和2被抛到主线程执行,这次它们执行时间略长,超过了一个帧间隔,导致订阅下一个信号的函数迟迟未被执行。对于上层来说,错过了第二个onVsync()
回调,这意味着,任务1和2错过了一次显示时机,任务3错过了一次渲染时机。对于底层来说,显示器在发出垂直同步信号时会向图形缓冲取显示内容,这次没拿到内容,只能继续显示上一帧图像。
任务1和2执行完,主线程才“订阅下一个信号”,当第三个信号到来时,显示器从图形缓冲中取到了任务1和2的渲染结果,而任务3也被抛到主线程执行。
这个 case 中,任务链中所有的绘制任务都被推迟显示了。
合并
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后说一下我的学习路线
其实很简单就下面这张图,含概了Android所有需要学的知识点,一共8大板块:
- 架构师筑基必备技能
- Android框架体系架构(高级UI+FrameWork源码)
- 360°Androidapp全方位性能调优
- 设计思想解读开源框架
- NDK模块开发
- 移动架构师专题项目实战环节
- 移动架构师不可不学习微信小程序
- 混合开发的flutter
Android学习的资料
我呢,把上面八大板块的分支都系统的做了一份学习系统的资料和视频,大概就下面这些,我就不全部写出来了,不然太长了影响大家的阅读。
330页PDF Android学习核心笔记(内含上面8大板块)
Android学习的系统对应视频
总结
我希望通过我自己的学习方法来帮助大家去提升技术:
-
1、多看书、看源码和做项目,平时多种总结
-
2、不能停留在一些基本api的使用上,应该往更深层次的方向去研究,比如activity、view的内部运行机制,比如Android内存优化,比如aidl,比如JNI等,并不仅仅停留在会用,而要通过阅读源码,理解其实现原理
-
3、同时对架构是有一定要求的,架构是抽象的,但是设计模式是具体的,所以一定要加强下设计模式的学习
-
4、android的方向也很多,高级UI,移动架构师,数据结构与算法和音视频FFMpeg解码,如果你对其中一项比较感兴趣,就大胆的进阶吧!
希望大家多多点赞,转发,评论加关注,你们的支持就是我继续下去的动力!加油!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
id框架体系架构(高级UI+FrameWork源码)**
3. 360°Androidapp全方位性能调优
4. 设计思想解读开源框架
5. NDK模块开发
6. 移动架构师专题项目实战环节
7. 移动架构师不可不学习微信小程序
8. 混合开发的flutter
[外链图片转存中…(img-Vr8Avzpb-1713234832318)]
Android学习的资料
我呢,把上面八大板块的分支都系统的做了一份学习系统的资料和视频,大概就下面这些,我就不全部写出来了,不然太长了影响大家的阅读。
330页PDF Android学习核心笔记(内含上面8大板块)
[外链图片转存中…(img-3pbjmXwm-1713234832319)]
Android学习的系统对应视频
总结
我希望通过我自己的学习方法来帮助大家去提升技术:
-
1、多看书、看源码和做项目,平时多种总结
-
2、不能停留在一些基本api的使用上,应该往更深层次的方向去研究,比如activity、view的内部运行机制,比如Android内存优化,比如aidl,比如JNI等,并不仅仅停留在会用,而要通过阅读源码,理解其实现原理
-
3、同时对架构是有一定要求的,架构是抽象的,但是设计模式是具体的,所以一定要加强下设计模式的学习
-
4、android的方向也很多,高级UI,移动架构师,数据结构与算法和音视频FFMpeg解码,如果你对其中一项比较感兴趣,就大胆的进阶吧!
希望大家多多点赞,转发,评论加关注,你们的支持就是我继续下去的动力!加油!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!