前面有学习了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()主要做了两件事:

  1. 往当前线程的Loop加入同步屏障
  2. 封装了一个mTraversalRunnable加入到mChoreographer

这里就引申出来三个问题:

  1. mTraversalRunnable是什么?
  2. mChoreographer是什么?
  3. 同步屏障是什么?有什么用?

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,里面做了两件事情:

  1. 移除同步屏障
  2. 开始真正的绘制流程。

总结而言就是一个图:
Android 屏幕刷新机制:ViewRootImpl、Choreographer、Surface、SurfaceFlinger关系_第1张图片
那上面我们知道,Choreographer加入了TraversalRunnable,那什么时候去执行TraversalRunnable呢?

Choreographer

Choreographer 翻译为编舞者,负责从显示系统接收脉冲信号,编排渲染下一帧的绘制工作,负责获取Vsync同步信号并控制UI线程完成图像绘制的类。

怎么理解这一句话呢?

一次完整的绘制,是需要CPU、GPU和显示设备的配合,但是三者是一个并行运作的状态,那怎么相互协调呢?所以引进了VSync信号机制:每16ms,硬件(或者软件)会发出一个VSync信号,CPU接收到这个信号,开始了一次绘制流程。再下一次VSync信号到来之时,Display就可以直接显示第一帧,CPU也开始绘制第二帧。就这样循环下去。

也就是说CPU和GPU必须要在这一次的VSync信号发生和下一次VSync信号到来之前要准备好这一帧的数据,否则就发生了掉帧的现象了。

可以看下图,大致可以了解VSync信号机制:
Android 屏幕刷新机制:ViewRootImpl、Choreographer、Surface、SurfaceFlinger关系_第2张图片

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);

可以看到主要是两个动作:

  1. mCallbackQueues加入了Runnable
  2. 从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信号来临的时候,主要做了两件事情

  1. 从mCallbackQueues取出callback
  2. 执行callback,这里最后会执行到TraservalRunnable的三大绘制流程

这里是不是说,Choreographer是不是只要注册一次以后,都可以收到Vsync信号呢?

其实不是的,Vsync信号需要每次都去注册,而且只能接收到一次。这样能保证在不需要重新绘制的情况下,Choreographer也就不需要接收信号,就不会去注册Vsync信号。
Android 屏幕刷新机制:ViewRootImpl、Choreographer、Surface、SurfaceFlinger关系_第3张图片

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优先被执行。
Android 屏幕刷新机制:ViewRootImpl、Choreographer、Surface、SurfaceFlinger关系_第4张图片

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

更多相关文章

  1. Android与WebWiew的同步和异步访问机制
  2. Android 的事件分发机制
  3. Android消息机制---Handler工作原理
  4. Android之安全机制
  5. Android性能:经典ListView适配器convertView缓存及复用机制
  6. android 6.0 logcat机制(三)logd处理请求log
  7. Android Binder机制----实现自定义的系统服务

随机推荐

  1. input系统一 loop线程的创建与运行
  2. android监听短信
  3. 线性布局的常用属性
  4. Android(安卓)Manifest.xml 分析
  5. 传智播客Android视频教程——第四天
  6. RelativeLayout参数意义
  7. Android(安卓)RatingBar自定义替换系统图
  8. android 字体总结
  9. [1] Android主要源代码组成
  10. 【Android】Android部分问题记录