Android 屏幕刷新机制:ViewRootImpl、Choreographer、Surface、SurfaceFlinger关系
前面有学习了Android绘制的三大流程:merge、layout、draw,但是一直都没有搞清楚绘制到显示的整体流程。借此机会,记录下自己学习过程。
我们都知道,一次完整的录制时都是从ViewRootImpl的scheduleTraversals()开始,即使调用invalidate()也是如此。
scheduleTraversals
// ViewRootImpl.java void scheduleTraversals() { // mTraversalScheduled阻止了多次调用 if (!mTraversalScheduled) { mTraversalScheduled = true; // 加入同步屏障 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 加入一个callback到mChoreographer mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
可以看到scheduleTraversals()主要做了两件事:
- 往当前线程的Loop加入同步屏障
- 封装了一个mTraversalRunnable加入到mChoreographer
这里就引申出来三个问题:
- mTraversalRunnable是什么?
- mChoreographer是什么?
- 同步屏障是什么?有什么用?
mTraversableRunnable
其实TraversableRunnable分装着就是measure、layout、draw三大流程。
// ViewRootImplfinal class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); }}void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; // 移除同步屏障 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); performTraversals(); }}private void performTraversals() { final View host = mView;performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);performLayout(lp, mWidth, mHeight); performDraw();}
可见,TraversalRunnable就是一个Runnable,里面做了两件事情:
- 移除同步屏障
- 开始真正的绘制流程。
总结而言就是一个图:
那上面我们知道,Choreographer加入了TraversalRunnable,那什么时候去执行TraversalRunnable呢?
Choreographer
Choreographer 翻译为编舞者,负责从显示系统接收脉冲信号,编排渲染下一帧的绘制工作,负责获取Vsync同步信号并控制UI线程完成图像绘制的类。
怎么理解这一句话呢?
一次完整的绘制,是需要CPU、GPU和显示设备的配合,但是三者是一个并行运作的状态,那怎么相互协调呢?所以引进了VSync信号机制:每16ms,硬件(或者软件)会发出一个VSync信号,CPU接收到这个信号,开始了一次绘制流程。再下一次VSync信号到来之时,Display就可以直接显示第一帧,CPU也开始绘制第二帧。就这样循环下去。
也就是说CPU和GPU必须要在这一次的VSync信号发生和下一次VSync信号到来之前要准备好这一帧的数据,否则就发生了掉帧的现象了。
可以看下图,大致可以了解VSync信号机制:
Choreographer怎么跟Vsync信号机制相挂钩呢?
Choreographer负责订阅Vsync信号和收到Vsycn信号时候,负责去开始一次绘制,具体我们可以看下源码:
我们从上面的ViewRootImpl将mTraversalRunnable封装加入mChoreographer中入手:
// ViewRootImplmChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);// Choreographerpublic void postCallback(int callbackType, Runnable action, Object token) { postCallbackDelayed(callbackType, action, token, 0);}private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; // 加入mCallbackQueues mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);// 这里的delayMills为0,所以会走到scheduleFrameLocked() if (dueTime <= now) { scheduleFrameLocked(now); } ... }}private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; ... // 判断当前thread是否运行loop,这个后续会讲到 if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); } }}private void scheduleVsyncLocked() { // 注册订阅了vsync信号 mDisplayEventReceiver.scheduleVsync();}// DisplayEventReceiverpublic void scheduleVsync() { ... // 注册订阅了vsync信号 nativeScheduleVsync(mReceiverPtr);}private static native void nativeScheduleVsync(long receiverPtr);
可以看到主要是两个动作:
- mCallbackQueues加入了Runnable
- 从jni层注册了Vsync信号
那什么时候会收到Vsync信号回来呢?
在DisplayEventReceiver的disjpatchVsync()方法可以看到,该方法是jni层调用的
// DisplayEventReceiver,由jni调用private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) { onVsync(timestampNanos, builtInDisplayId, frame);}//Choreographer内部类DisplayEventReceiver,重写了onVsync方法@Overridepublic void onVsync(long timestampNanos, int builtInDisplayId, 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);}
这里可以看到,其实mHandler就是当前主线程的handler,当接收到onVsync信号的时候,将自己封装到Message中,等到Looper处理,最后Looper处理消息的时候就会调用run方法,这里是Handler的机制,不做解释。
在run方法中,调用了doFrame()方法:
// Choreographervoid doFrame(long frameTimeNanos, int frame) {...doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);}void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; // 从mCallbackQueues取出 callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); for (CallbackRecord c = callbacks; c != null; c = c.next) { c.run(frameTimeNanos); }}// CallbackRecordpublic void run(long frameTimeNanos) { if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { // 这里也即是调用了TraservalRunnable的run方法,也即是三个绘制流程 ((Runnable)action).run(); }}
可见当vsync信号来临的时候,主要做了两件事情
- 从mCallbackQueues取出callback
- 执行callback,这里最后会执行到TraservalRunnable的三大绘制流程
这里是不是说,Choreographer是不是只要注册一次以后,都可以收到Vsync信号呢?
其实不是的,Vsync信号需要每次都去注册,而且只能接收到一次。这样能保证在不需要重新绘制的情况下,Choreographer也就不需要接收信号,就不会去注册Vsync信号。
Choreographer在哪里初始化的?
以上介绍了Choreographer是如何发挥作用的,但是Choregrapher是在哪里初始化的?
// Choreographer public static Choreographer getInstance() { return sThreadInstance.get(); } private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue() { Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalStateException("The current thread must have a looper!"); } Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP); if (looper == Looper.getMainLooper()) { mMainInstance = choreographer; } return choreographer; }};
由上面的代码可以看到:Choreographer存在于Looper的线程的ThreadLocal中,每个looper的线程可能都有一个Choreographer。
而且在ViewRootImpl中可以看到他的身影,这部分不继续介绍了。
同步屏障
上面出现了三个东西关于同步屏障:
1. 加入同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
2. 移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
3. 设置成异步消息
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
同步屏障:
顾名思义就是在阻止同步消息的运行,而异步消息可以直接运行。而我们日常使用的Message都是同步消息。
这得从Handler的机制说起,在MessageQueue中有一个消息链表,类似下图,我们的Message会根据执行时间排序,在next()方法依次取出下一个需要执行的Message,如果加入了同步屏障,则会滤过同步消息,直接取异步消息执行,直到屏障被移除掉。
** 为什么要这么做呢?**
原因是确保异步消息尽可能快的被执行,在这里是确保mTraversalRunnable优先被执行。
Surface 简单了解
以上只是介绍了绘制流程怎么跟VSync信号相结合,但是并没有说到绘制流程怎么显示到显示器中,这个涉及到Surface的概念,这部分我只是了解了一部分。
surface 存在于ViewRootImpl中
// ViewRootImplpublic final Surface mSurface = new Surface();// Surfacepublic Surface() {}
但是,Surface 还没真正的初始化,这里并没有这么简单,他是连接显示系统的关键,真正的初始化在WMS中,在Acitivity中有讲到窗口机制,在WMS创建对应的Surface,再映射到ViewRootImpl的Surface中。ViewRoot对Surface绘制,WMS对Surface初始化及操作。
而在ViewRootImpl中draw方法中会调用drawSoftware,内部实际上是对Surface的canvas的绘制
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets) {...canvas = mSurface.lockCanvas(dirty);...mView.draw(canvas); ... surface.unlockCanvasAndPost(canvas);}
以上大致是,ViewRootImpl会绘制操作到Surface,而Surface是跟WMS共享的,
SurfaceFinger 简单了解
SurfaceFlinger是运行在一个独立的进程,由init进程启动。
WMS在创建Surface的时候会通过SurfaceSession中间会话层去请求SurfaceFlinger去创建的对应的Layer
SurfaceFlinger 会维持Layer的Z轴序列,并对Layer最终计算输出
对于SurfaceFlinger太复杂了,后面需要去更多的了解。
Choreographer 做帧率检测
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { checkFramesInternal(frameTimeNanos); Choreographer.getInstance().postFrameCallback(this); } });
参考
http://gityuan.com/2017/02/25/choreographer/
https://www.jianshu.com/p/75139692b8e6
https://blog.csdn.net/windskier/article/details/7041610
更多相关文章
- Android与WebWiew的同步和异步访问机制
- Android 的事件分发机制
- Android消息机制---Handler工作原理
- Android之安全机制
- Android性能:经典ListView适配器convertView缓存及复用机制
- android 6.0 logcat机制(三)logd处理请求log
- Android Binder机制----实现自定义的系统服务