主线程运行的本质,其实就是 Message 的处理过程,咱们的各类操做,包括每一帧的渲染操做 ,都是经过 Message 的形式发给主线程的 MessageQueue ,MessageQueue 处理完消息继续等下一个消息,以下图所示java
MethodTrace 图示android
Systrace 图示git
引入 Vsync 以前的 Android 版本,渲染一帧相关的 Message ,中间是没有间隔的,上一帧绘制完,下一帧的 Message 紧接着就开始被处理。这样的问题就是,帧率不稳定,可能高也可能低,不稳定,以下图github
MethodTrace 图示shell
Systrace 图示app
对于用户来讲,稳定的帧率才是好的体验,好比你玩王者荣耀,相比 fps 在 60 和 40 之间频繁变化,用户感受更好的是稳定在 50 fps 的状况。异步
因此 Android 的演进中,引入了 Vsync + TripleBuffer + Choreographer 的机制,其主要目的就是提供一个稳定的帧率输出机制,让软件层和硬件层能够以共同的频率一块儿工做。ide
Choreographer 的引入,主要是配合 Vsync ,给上层 App 的渲染提供一个稳定的 Message 处理的时机,也就是 Vsync 到来的时候 ,系统经过对 Vsync 信号周期的调整,来控制每一帧绘制操做的时机. 目前大部分手机都是 60Hz 的刷新率,也就是 16.6ms 刷新一次,系统为了配合屏幕的刷新频率,将 Vsync 的周期也设置为 16.6 ms,每一个 16.6 ms , Vsync 信号唤醒 Choreographer 来作 App 的绘制操做 ,这就是引入 Choreographer 的主要做用函数
Choreographer 扮演 Android 渲染链路中承上启下的角色工具
从上面能够看出来, Choreographer 担任的是一个工具人的角色,他之因此重要,是由于经过 Choreographer + SurfaceFlinger + Vsync + TripleBuffer 这一套从上到下的机制,保证了 Android App 能够以一个稳定的帧率运行(目前大部分是 60fps),减小帧率波动带来的不适感.
了解 Choreographer 还能够帮助 App 开发者知道程序每一帧运行的基本原理,也能够加深对 Message、Handler、Looper、MessageQueue、Measure、Layout、Draw 的理解 , 不少 APM 工具也用到了 Choreographer( 利用 FrameCallback + FrameInfo ) + MessageQueue ( 利用 IdleHandler ) + Looper ( 设置自定义 MessageLogging) 这些组合拳,深刻了解了这些以后,再去作优化,脑子里的思路会更清晰。
另外虽然画图是一个比较好的解释流程的好路子,可是我我的不是很喜欢画图,由于平时 Systrace 和 MethodTrace 用的比较多,Systrace 是按从左到右展现整个系统的运行状况的一个工具(包括 cpu、SurfaceFlinger、SystemServer、App 等关键进程),使用 Systrace 和 MethodTrace 也能够很方便地展现关键流程。当你对系统代码比较熟悉的时候,看 Systrace 就能够和手机运行的实际状况对应起来。因此下面的文章除了一些网图以外,其余的我会多以 Systrace 来展现.
下图以滑动桌面为例子,咱们先看一下从左到右滑动桌面的一个完整的预览图(App 进程),能够看到 Systrace 中从左到右,每个绿色的帧都表示一帧,表示最终咱们能够手机上看到的画面
有了上面这个总体的概念,咱们将 UI Thread 的每一帧放大来看,看看 Choreogrepher 的位置以及 Choreogrepher 是怎么组织每一帧的
第一步初始化完成后,后续就会在步骤 2-9 之间循环
同时也附上这一帧所对应的 MethodTrace(这里预览一下便可,下面会有详细的大图)
下面咱们就从源码的角度,来看一下具体的实现
// Thread local storage for the choreographer.
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
// 获取当前线程的 Looper
Looper looper = Looper.myLooper();
......
// 构造 Choreographer 对象
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};
复制代码
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
// 1. 初始化 FrameHandler
mHandler = new FrameHandler(looper);
// 2. 初始化 DisplayEventReceiver
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
//3. 初始化 CallbacksQueues
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
......
}
复制代码
private final class FrameHandler extends Handler {
......
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME://开始渲染下一帧的操做
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC://请求 Vsync
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK://处理 Callback
doScheduleCallback(msg.arg1);
break;
}
}
}
复制代码
在 Activity 启动过程,执行完 onResume 后,会调用 Activity.makeVisible(),而后再调用到 addView(), 层层调用会进入以下方法
ActivityThread.handleResumeActivity(IBinder, boolean, boolean, String) (android.app)
-->WindowManagerImpl.addView(View, LayoutParams) (android.view)
-->WindowManagerGlobal.addView(View, LayoutParams, Display, Window) (android.view)
-->ViewRootImpl.ViewRootImpl(Context, Display) (android.view)
public ViewRootImpl(Context context, Display display) {
......
mChoreographer = Choreographer.getInstance();
......
}
复制代码
Vsync 的注册和回调经过 FrameDisplayEventReceiver 这个类,因此能够先简单介绍一下。 FrameDisplayEventReceiver 继承 DisplayEventReceiver , 有三个比较重要的方法
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
......
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
......
mTimestampNanos = timestampNanos;
mFrame = 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 void scheduleVsync() {
......
nativeScheduleVsync(mReceiverPtr);
......
}
}
复制代码
从下面的函数调用栈能够看到,Choreographer 的内部类 FrameDisplayEventReceiver.onVsync 负责接收 Vsync 回调,通知 UIThread 进行数据处理。
那么 FrameDisplayEventReceiver 是经过什么方式在 Vsync 信号到来的时候回调 onVsync 呢?答案是 FrameDisplayEventReceiver 的初始化的时候,最终经过监听文件句柄的形式,其对应的初始化流程以下
android/view/Choreographer.java
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
......
}
复制代码
android/view/Choreographer.java
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource);
}
复制代码
android/view/DisplayEventReceiver.java
public DisplayEventReceiver(Looper looper, int vsyncSource) {
......
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource);
}
复制代码
nativeInit 后续的代码能够本身跟一下,能够对照这篇文章和源码,因为篇幅比较多,这里就不细写了(www.jianshu.com/p/304f56f5d…) , 后续梳理好这一块的逻辑后,会在另外的文章更新。
DisplayEventReceiver 初始化完成后,Choreographer 与 SurfaceFlinger 的 Vsync 信号传递通道就创建好了,后续有 Vsync 信号到来时,就能够沿着这条通道,一路回调到 Java 层的 Choreographer.FrameDisplayEventReceiver.onVsync 方法了。
Choreographer 处理绘制的逻辑核心在 Choreographer.doFrame 函数中,从下图能够看到,FrameDisplayEventReceiver.onVsync post 了本身,其 run 方法直接调用了 doFrame 开始一帧的逻辑处理
android/view/Choreographer.java
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
......
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
复制代码
doFrame 函数主要作下面几件事
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
......
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
}
......
}
......
}
复制代码
Choreographer.doFrame 的掉帧检测比较简单,从下图能够看到,Vsync 信号到来的时候会标记一个 start_time ,执行 doFrame 的时候标记一个 end_time ,这两个时间差就是 Vsync 处理时延,也就是掉帧
咱们以 Systrace 的掉帧的实际状况来看掉帧的计算逻辑
这里须要注意的是,这种方法计算的掉帧,是前一帧的掉帧状况,而不是这一帧的掉帧状况,这个计算方法是有缺陷的,会致使有的掉帧没有被计算到
Choreographer 中 FrameInfo 来负责记录帧的绘制信息,doFrame 执行的时候,会把每个关键节点的绘制时间记录下来,咱们使用 dumpsys gfxinfo 就能够看到。固然 Choreographer 只是记录了一部分,剩余的部分在 hwui 那边来记录。
从 FrameInfo 这些标志就能够看出记录的内容,后面咱们看 dumpsys gfxinfo 的时候数据就是按照这个来排列的
// Various flags set to provide extra metadata about the current frame
private static final int FLAGS = 0;
// Is this the first-draw following a window layout?
public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1;
// A renderer associated with just a Surface, not with a ViewRootImpl instance.
public static final long FLAG_SURFACE_CANVAS = 1 << 2;
@LongDef(flag = true, value = {
FLAG_WINDOW_LAYOUT_CHANGED, FLAG_SURFACE_CANVAS })
@Retention(RetentionPolicy.SOURCE)
public @interface FrameInfoFlags {}
// The intended vsync time, unadjusted by jitter
private static final int INTENDED_VSYNC = 1;
// Jitter-adjusted vsync time, this is what was used as input into the
// animation & drawing system
private static final int VSYNC = 2;
// The time of the oldest input event
private static final int OLDEST_INPUT_EVENT = 3;
// The time of the newest input event
private static final int NEWEST_INPUT_EVENT = 4;
// When input event handling started
private static final int HANDLE_INPUT_START = 5;
// When animation evaluations started
private static final int ANIMATION_START = 6;
// When ViewRootImpl#performTraversals() started
private static final int PERFORM_TRAVERSALS_START = 7;
// When View:draw() started
private static final int DRAW_START = 8;
复制代码
doFrame 函数记录从 Vsync time 到 markPerformTraversalsStart 的时间
void doFrame(long frameTimeNanos, int frame) {
......
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
// 处理 CALLBACK_INPUT Callbacks
mFrameInfo.markInputHandlingStart();
// 处理 CALLBACK_ANIMATION Callbacks
mFrameInfo.markAnimationsStart();
// 处理 CALLBACK_INSETS_ANIMATION Callbacks
// 处理 CALLBACK_TRAVERSAL Callbacks
mFrameInfo.markPerformTraversalsStart();
// 处理 CALLBACK_COMMIT Callbacks
......
}
复制代码
void doFrame(long frameTimeNanos, int frame) {
......
// 处理 CALLBACK_INPUT Callbacks
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
// 处理 CALLBACK_ANIMATION Callbacks
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
// 处理 CALLBACK_INSETS_ANIMATION Callbacks
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
// 处理 CALLBACK_TRAVERSAL Callbacks
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
// 处理 CALLBACK_COMMIT Callbacks
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
......
}
复制代码
Input 回调调用栈
**input callback 通常是执行 **ViewRootImpl.ConsumeBatchedInputRunnable
android/view/ViewRootImpl.java
final class ConsumeBatchedInputRunnable implements Runnable {
@Override
public void run() {
doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
}
}
void doConsumeBatchedInput(long frameTimeNanos) {
if (mConsumeBatchedInputScheduled) {
mConsumeBatchedInputScheduled = false;
if (mInputEventReceiver != null) {
if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
&& frameTimeNanos != -1) {
scheduleConsumeBatchedInput();
}
}
doProcessInputEvents();
}
}
复制代码
Input 时间通过处理,最终会传给 DecorView 的 dispatchTouchEvent,这就到了咱们熟悉的 Input 事件分发
Animation 回调调用栈
通常咱们接触的多的是调用 View.postOnAnimation 的时候,会使用到 CALLBACK_ANIMATION
public void postOnAnimation(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.mChoreographer.postCallback(
Choreographer.CALLBACK_ANIMATION, action, null);
} else {
// Postpone the runnable until we know
// on which thread it needs to run.
getRunQueue().post(action);
}
}
复制代码
那么通常是何时回调用到 View.postOnAnimation 呢,我截取了一张图,你们能够本身去看一下,接触最多的应该是 startScroll,Fling 这种操做
其调用栈根据其 post 的内容,下面是桌面滑动松手以后的 fling 动画。
另外咱们的 Choreographer 的 FrameCallback 也是用的 CALLBACK_ANIMATION
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis);
}
复制代码
Traversal 调用栈
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//为了提升优先级,先 postSyncBarrier
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
// 真正开始执行 measure、layout、draw
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 这里把 SyncBarrier remove
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
// 真正开始
performTraversals();
}
}
private void performTraversals() {
// measure 操做
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || contentInsetsChanged || updatedConfiguration) {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
// layout 操做
if (didLayout) {
performLayout(lp, mWidth, mHeight);
}
// draw 操做
if (!cancelDraw && !newSurface) {
performDraw();
}
}
复制代码
doTraversal 的 TraceView 示例
因为动画、滑动、Fling 这些操做的存在,咱们须要一个连续的、稳定的帧率输出机制。这就涉及到了 Vsync 的请求逻辑,在连续的操做,好比动画、滑动、Fling 这些状况下,每一帧的 doFrame 的时候,都会根据状况触发下一个 Vsync 的申请,这样咱们就能够得到连续的 Vsync 信号。咱们下面以 Animation 为例,看看 Animation 是如何驱动下一个 Vsync ,来持续更新画面的
android/animation/ObjectAnimator.java
public void start() {
super.start();
}
复制代码
android/animation/ValueAnimator.java
private void start(boolean playBackwards) {
......
addAnimationCallback(0); // 动画 start 的时候添加 Animation Callback
......
}
private void addAnimationCallback(long delay) {
......
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
复制代码
android/animation/AnimationHandler.java
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
// post FrameCallback
getProvider().postFrameCallback(mFrameCallback);
}
......
}
// 这里的 mFrameCallback 回调 doFrame,里面 post了本身
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
// post 本身
getProvider().postFrameCallback(this);
}
}
};
复制代码
调用 postFrameCallback 会走到 mChoreographer.postFrameCallback ,这里就会触发 Choreographer 的 Vsync 请求逻辑
android/animation/AnimationHandler.java
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
复制代码
android/view/Choreographer.java
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
// 请求 Vsync scheduleFrameLocked ->scheduleVsyncLocked-> mDisplayEventReceiver.scheduleVsync ->nativeScheduleVsync
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
复制代码
经过上面的 Animation.start 设置,利用了 Choreographer.FrameCallback 接口,每一帧都去请求下一个 Vsync 动画过程当中一帧的 TraceView 示例
因为 Choreographer 的位置,许多性能监控的手段都是利用 Choreographer 来作的,除了自带的掉帧计算,Choreographer 提供的 FrameCallback 和 FrameInfo 都给 App 暴露了接口,让 App 开发者能够经过这些方法监控自身 App 的性能,其中经常使用的方法以下:
public interface FrameCallback {
public void doFrame(long frameTimeNanos);
}
复制代码
Choreographer.getInstance().postFrameCallback(youOwnFrameCallback );
复制代码
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
......
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis);
}
复制代码
TinyDancer 就是使用了这个方法来计算 FPS (github.com/friendlyrob…)
adb shell dumpsys gfxinfo framestats
Window: StatusBar
Stats since: 17990256398ns
Total frames rendered: 1562
Janky frames: 361 (23.11%)
50th percentile: 6ms
90th percentile: 23ms
95th percentile: 36ms
99th percentile: 101ms
Number Missed Vsync: 33
Number High input latency: 683
Number Slow UI thread: 273
Number Slow bitmap uploads: 8
Number Slow issue draw commands: 18
Number Frame deadline missed: 287
HISTOGRAM: 5ms=670 6ms=128 7ms=84 8ms=63 9ms=38 10ms=23 11ms=21 12ms=20 13ms=25 14ms=39 15ms=65 16ms=36 17ms=51 18ms=37 19ms=41 20ms=20 21ms=19 22ms=18 23ms=15 24ms=14 25ms=8 26ms=4 27ms=6 28ms=3 29ms=4 30ms=2 31ms=2 32ms=6 34ms=12 36ms=10 38ms=9 40ms=3 42ms=4 44ms=5 46ms=8 48ms=6 53ms=6 57ms=4 61ms=1 65ms=0 69ms=2 73ms=2 77ms=3 81ms=4 85ms=1 89ms=2 93ms=0 97ms=2 101ms=1 105ms=1 109ms=1 113ms=1 117ms=1 121ms=2 125ms=1 129ms=0 133ms=1 150ms=2 200ms=3 250ms=0 300ms=1 350ms=1 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0 650ms=0
---PROFILEDATA---
Flags,IntendedVsync,Vsync,OldestInputEvent,NewestInputEvent,HandleInputStart,AnimationStart,PerformTraversalsStart,DrawStart,SyncQueued,SyncStart,IssueDrawCommandsStart,SwapBuffers,FrameCompleted,DequeueBufferDuration,QueueBufferDuration,
0,10158314881426,10158314881426,9223372036854775807,0,10158315693363,10158315760759,10158315769821,10158316032165,10158316627842,10158316838988,10158318055915,10158320387269,10158321770654,428000,773000,
0,10158332036261,10158332036261,9223372036854775807,0,10158332799196,10158332868519,10158332877269,10158333137738,10158333780654,10158333993206,10158335078467,10158337689561,10158339307061,474000,885000,
0,10158348665353,10158348665353,9223372036854775807,0,10158349710238,10158349773102,10158349780863,10158350405863,10158351135967,10158351360446,10158352300863,10158354305654,10158355814509,471000,836000,
0,10158365296729,10158365296729,9223372036854775807,0,10158365782373,10158365821019,10158365825238,10158365975290,10158366547946,10158366687217,10158367240706,10158368429248,10158369291852,269000,476000,
复制代码
命令解释:
数据:
掉帧 jank 计算
每一行均可以经过下面的公式获得一个值,该值是一个标准,咱们称为jankflag,若是当前行的jankflag与上一行的jankflag发生改变,那么就叫掉帧
ceil((C - A) / refresh-period)
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
mFlinger.transact(1013, data, reply, 0);
final int pageFlipCount = reply.readInt();
final long now = System.nanoTime();
final int frames = pageFlipCount - mLastPageFlipCount;
final long duration = now - mLastUpdateTime;
mFps = (float) (frames * 1e9 / duration);
mLastPageFlipCount = pageFlipCount;
mLastUpdateTime = now;
reply.recycle();
data.recycle();
复制代码
SKIPPED_FRAME_WARNING_LIMIT 默认为30 , 应用能够获取 hook 这个值进行修改
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
}
复制代码
App 能够经过 hook 修改这个默认值,以便根据这个来监控性能
Field field = Choreographer.class.getDeclaredField("SKIPPED_FRAME_WARNING_LIMIT");
field.setAccessible(true);
field.set(Choreographer.class, 0);
复制代码
Blockcanary 计算作性能监控使用的是 Looper 的消息机制,经过对 MessageQueue 中每个 Message 的先后进行记录,打到监控性能的目的
android/os/Looper.java
public static void loop() {
...
for (;;) {
...
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
...
}
}
复制代码
所谓的异步消息其实就是这样的,咱们能够经过 enqueueBarrier 往消息队列中插入一个 Barrier,那么队列中执行时间在这个 Barrier 之后的同步消息都会被这个 Barrier 拦截住没法执行,直到咱们调用 removeBarrier 移除了这个 Barrier,而异步消息则没有影响,消息默认就是同步消息,除非咱们调用了 Message 的 setAsynchronous,这个方法是隐藏的。只有在初始化 Handler 时经过参数指定往这个 Handler 发送的消息都是异步的,这样在 Handler 的 enqueueMessage 中就会调用 Message 的 setAsynchronous 设置消息是异步的,从上面 Handler.enqueueMessage 的代码中能够看到。
所谓异步消息,其实只有一个做用,就是在设置 Barrier 时仍能够不受 Barrier 的影响被正常处理,若是没有设置 Barrier,异步消息就与同步消息没有区别,能够经过 removeSyncBarrier 移除 Barrier
scheduleTraversals 的时候 postSyncBarrier
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//为了提升优先级,先 postSyncBarrier
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
复制代码
doTraversal 的时候 removeSyncBarrier
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 这里把 SyncBarrier remove
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
// 真正开始
performTraversals();
}
}
复制代码
Choreographer post Message 的时候,会把这些消息设为 Asynchronous ,这样Choreographer 中的这些 Message 的优先级就会比较高,
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
复制代码
系统厂商因为能够直接修改源码,也利用这方面的便利,作一些功能和优化,不过因为保密的问题,代码就不直接放上来了,我能够大概说一下思路,感兴趣的能够私下讨论
Choreographer 自己是没有 input 消息的, 不过修改源码以后,input 消息能够直接给到 Choreographer 这里, 有了这些 Input 消息,Choreographer 就能够作一些事情,好比说提早响应,不去等 Vsync
当一个 Android App 退到后台以后,只要他没有被杀死,那么他作什么事情你们都不要奇怪,由于这就是 Android。有的 App 退到后台以后还在持续调用 Choreographer 中的 Animation Callback,而这个 Callback 的执行彻底是无心义的,并且用户还不知道,可是对 cpu 的占用是比较高的。
因此在 Choreographer 中会针对这种状况作优化,禁止不符合条件的 App 在后台继续无用的操做
和移动事件优化同样,因为有了 Input 事件的信息,在某些场景下咱们能够通知 SurfaceFlinger 不用取等待 Vsync 直接作合成操做
咱们前面说,主线程的全部操做都是给予 Message 的 ,若是某个操做,非重要的 Message 被排列到了队列后面,那么对这个操做产生影响;而经过从新排列 MessageQueue,在应用启动的时候,把启动相关的重要的启动 Message 放到队列前面,来起到加快启动速度的做用
90 fps 的手机上 , Vsync 间隔从 16.6ms 变成了 11.1ms ,这带来了巨大的性能和功耗挑战,如何在一帧内完成渲染的必要操做,是手机厂商必需要思考和优化的地方:
点赞或者交流,能够移步本文的知乎界面 知乎 - Android 基于 Choreographer 的渲染机制详解
小厂系统研发工程师 , 更多信息能够点击 关于我 , 很是但愿和你们一块儿交流 , 共同进步 .