起源: 读书:- 2021-04-04 12:53:45
一、途径程电脑运行策略的本体论二、Choreographer 详细介绍三、Choreographer 源码解答四、APM 与 Choreographer五、MessageQueue 与 Choreographer六、移动制造厂商优化调整本篇文章介紹了 App 開發者不长时间接受到其实在 Android Framework 效果图渲染线路中异常决定性的很多类 Choreographer。其中包括 Choreographer 的传入底色、Choreographer 的简洁、很多源码解析视频、Choreographer 与 MessageQueue、Choreographer 和 APM,已经平果手机生产商对于 Choreographer 的很多升级优化一个构想。Choreographer 的机遇,主耍是合作 Vsync ,给顶层 App 的突出保证有一个保持稳定的 Message 处理的时间窗口,也说是说 Vsync 即将到来的时会 ,软件用对 Vsync 数据无线信号定期的变动,来控住每一帧制图作业的时间窗口. 现下大地方移动是 60Hz 的自动清空率,也说是说 16.6ms 自动清空每次,软件因为合作频幕的自动清空概率,将 Vsync 的定期也设为为 16.6 ms,每台 16.6 ms , Vsync 数据无线信号重启 Choreographer 来做 App 的制图作业 ,这说是说机遇 Choreographer 的主耍效应. 熟知 Choreographer 还需要帮助到 App 开发管理者都清楚软件每一帧正常运行的基本性道理,也需要加重对 Message、Handler、Looper、MessageQueue、Measure、Layout、Draw 的认知
MethodTrace 图示
Systrace 图示
MethodTrace 图示
Systrace 图示
所以 Android 的演进中,引入了 Vsync + TripleBuffer + Choreographer 的机制,其主要目的就是提供一个稳定的帧率输出机制,让软件层和硬件层可以以共同的频率一起工作。
从上面可以看出来, 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 来展示.
第一步初始化完成后,后续就会在步骤 2-9 之间循环
一起也附上这一帧所对应着的 MethodTrace(在这阅读一番就可以了,现在会起简要的大图)// Thread local storage for the choreographer.private static final ThreadLocal sThreadInstance = new ThreadLocal() { @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; } }}
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(); ...... }
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); ...... }}
private Choreographer(Looper looper, int vsyncSource) { mLooper = looper; mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null; ......}
android/view/Choreographer.javapublic FrameDisplayEventReceiver(Looper looper, int vsyncSource) { super(looper, vsyncSource);}
android/view/DisplayEventReceiver.javapublic DisplayEventReceiver(Looper looper, int vsyncSource) { ...... mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference(this), mMessageQueue, vsyncSource);}
nativeInit 随后的编号是可不可以自已跟看,是可不可以较这篇本文和源码,根据内文相对较多,这儿华祥苑茗茶小编就细写了(//www.jianshu.com/p/304f56f5d486) , 随后解读好这方面的思维模式后,会在其次的本文更行。简略我认为,FrameDisplayEventReceiver 的原始化环节中,实现 BitTube(实际是一名 socket pair),来信息传递和重定向 Vsync 案例,当 SurfaceFlinger 发送到到 Vsync 案例往后,实现 appEventThread 将你这个案例实现 BitTube 发送到 DisplayEventDispatcher ,DisplayEventDispatcher 实现 BitTube 的发送到端监视到 Vsync 案例往后,乖离率指标 Choreographer.FrameDisplayEventReceiver.onVsync ,促发就开始一帧的画制,方式图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 警报即将来临的时期会标志2个 start_time ,履行 doFrame 的时期标志2个 end_time ,这2个时差只是 Vsync 操作时延,也只是掉帧// Various flags set to provide extra metadata about the current frameprivate 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 jitterprivate static final int INTENDED_VSYNC = 1;// Jitter-adjusted vsync time, this is what was used as input into the// animation & drawing systemprivate static final int VSYNC = 2;// The time of the oldest input eventprivate static final int OLDEST_INPUT_EVENT = 3;// The time of the newest input eventprivate static final int NEWEST_INPUT_EVENT = 4;// When input event handling startedprivate static final int HANDLE_INPUT_START = 5;// When animation evaluations startedprivate static final int ANIMATION_START = 6;// When ViewRootImpl#performTraversals() startedprivate static final int PERFORM_TRAVERSALS_START = 7;// When View:draw() startedprivate 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.javafinal 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_ANIMATIONpublic 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 这一种操作的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 removemHandler.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 示例
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.javapublic 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.javapublic void postFrameCallback(Choreographer.FrameCallback callback) { mChoreographer.postFrameCallback(callback);}
android/view/Choreographer.javaprivate 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 ->nativeScheduleVsyncscheduleFrameLocked(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 示例
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/friendlyrobotnyc/TinyDancer)adb shell dumpsys gfxinfo Window: StatusBarStats since: 17990256398nsTotal frames rendered: 1562Janky frames: 361 (23.11%)50th percentile: 6ms90th percentile: 23ms95th percentile: 36ms99th percentile: 101msNumber Missed Vsync: 33Number High input latency: 683Number Slow UI thread: 273Number Slow bitmap uploads: 8Number Slow issue draw commands: 18Number Frame deadline missed: 287HISTOGRAM: 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();
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."); }}
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); } ... }}
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 removemHandler.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);
免责声明及提醒:此文内容为本网所转载企业宣传资讯,该相关信息仅为宣传及传递更多信息之目的,不代表本网站观点,文章真实性请浏览者慎重核实!任何投资加盟均有风险,提醒广大民众投资需谨慎!