美文网首页
Android UI刷新Framwork层流程

Android UI刷新Framwork层流程

作者: tigerzhao | 来源:发表于2019-10-28 15:02 被阅读0次

    看过电影都知道,连续的场景其实是一帧一帧的静态图像,手机显示也是这样,只不过大部分手机刷新率增加到了每秒60帧,手机显示图像是逐行刷新的,如果一页的图像没有刷新完成,这个时候又有新的图像开始刷新就会产生画面撕裂,android在4.1版本的时候引入了一个黄油计划,用来解决系统UI响应的问题,通过强制垂直同步VSYNC信号来使图像产生和显示达到步调一致。
    VSYNC信号是由底层驱动发出来的,上层应用接受这个信号的地方就是Choreographer类中的内部类FrameDisplayEventReceiver。
    可以自己写一个自定义View,然后在onDraw方法中设置断点,查看方法调用栈就会发现这其实是一个带有callback的消息,以下是方法调用栈的截图:


    1.png

    这个callback如下图:


    2.png
    也可以在模拟器中直接打断点到SDK的代码,更方便。
    然后在Handler源码中处理带有callback消息的代码是这样的:
     /**
         * Handle system messages here.
         */
        public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    handleCallback方法的实现如下:

        private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    而FrameDisplayEventReceiver类继承了Runnable接口,看下具体的实现

    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
                implements Runnable {
            private boolean mHavePendingVsync;
            private long mTimestampNanos;
            private int mFrame;
    
            public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
                super(looper, vsyncSource);
            }
    
            @Override
            public 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);
            }
    
            @Override
            public void run() {
                mHavePendingVsync = false;
                doFrame(mTimestampNanos, mFrame);
            }
        }
    

    也就是直接调用了run()方法中的doFrame()方法。看下doFrame()方法的实现

       void doFrame(long frameTimeNanos, int frame) {
            ...
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
                AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
    
                mFrameInfo.markInputHandlingStart();
                doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    
                mFrameInfo.markAnimationsStart();
                doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
    
                mFrameInfo.markPerformTraversalsStart();
                doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    
                doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
            } finally {
                AnimationUtils.unlockAnimationClock();
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
          ...
        }
    

    执行了doCallbacks()方法,UI刷新执行的是CALLBACK_TRAVERSAL类型的,下面贴上doCallbacks()方法代码

    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;
                }
                mCallbacksRunning = true;
               ...
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
                for (CallbackRecord c = callbacks; c != null; c = c.next) {
                    if (DEBUG_FRAMES) {
                        Log.d(TAG, "RunCallback: type=" + callbackType
                                + ", action=" + c.action + ", token=" + c.token
                                + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                    }
                    c.run(frameTimeNanos);
                }
            } finally {
                synchronized (mLock) {
                    mCallbacksRunning = false;
                    do {
                        final CallbackRecord next = callbacks.next;
                        recycleCallbackLocked(callbacks);
                        callbacks = next;
                    } while (callbacks != null);
                }
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    

    执行callbacks的run方法,而callbacks来自CallbackQueue数组,CallbackQueue本身是一个内部维护了CallbackRecord的链表,继续看这个链表的添加方法

    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) {
                    scheduleFrameLocked(now);
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                    msg.arg1 = callbackType;
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, dueTime);
                }
            }
        }
    

    最终调用这个方法,讲callback传进来的方法是

        public void postCallback(int callbackType, Runnable action, Object token) {
            postCallbackDelayed(callbackType, action, token, 0);
        }
    

    调用Choreographer类这个方法的地方是在ViewRootImpl类里面:

    void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    mTraversalRunnable就是刷新的callback

    final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }
        final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    

    doTraversal()的方法

    void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
    
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }
    

    这里面performTraversals()方法就是开启view界面布局的核心代码,里面执行了performMeasure(),performLayout()等view布局流程的代码。
    接着回到最前面,这个带有callback的消息是怎么产生的,这部分内容牵扯到了内核层的代码,而这块我不熟悉,因此参考了git袁的博客。
    先来看类Choreographer的内部类FrameDisplayEventReceiver的继承关系:

        private final class FrameDisplayEventReceiver extends DisplayEventReceiver
                implements Runnable
    

    继承了DisplayEventReceiver类,来看下DisplayEventReceiver类的构造函数

        /**
         * Creates a display event receiver.
         *
         * @param looper The looper to use when invoking callbacks.
         * @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values.
         * @param configChanged Whether to dispatch config changed events. Must be one of the
         * CONFIG_CHANGED_EVENT_* values.
         */
        public DisplayEventReceiver(Looper looper, int vsyncSource, int configChanged) {
            if (looper == null) {
                throw new IllegalArgumentException("looper must not be null");
            }
    
            mMessageQueue = looper.getQueue();
            mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
                    vsyncSource, configChanged);
    
            mCloseGuard.open("dispose");
        }
    

    在构造函数中调用了JNI方法nativeInit,将Java层的消息队列传进去了,此处属于内核部分的原理,能力有限就不做深究,内核层中产生的vysnc信号,最终会调用到Java层的DisplayEventReceiver类的dispatchVsync方法:

        // Called from native code.
        @SuppressWarnings("unused")
        @UnsupportedAppUsage
        private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
            onVsync(timestampNanos, physicalDisplayId, frame);
        }
    

    而onVsync方法在子类FrameDisplayEventReceiver中的实现就是创建了一个带有callback的msg:

            @Override
            public 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);
            }
    

    mHandler的初始化在Choreographer的构造方法中:

        private Choreographer(Looper looper, int vsyncSource) {
            mLooper = looper;
            mHandler = new FrameHandler(looper);
            mDisplayEventReceiver = USE_VSYNC
                    ? new FrameDisplayEventReceiver(looper, vsyncSource)
                    : null;
            mLastFrameTimeNanos = Long.MIN_VALUE;
    
            mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
    
            mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
            for (int i = 0; i <= CALLBACK_LAST; i++) {
                mCallbackQueues[i] = new CallbackQueue();
            }
            // b/68769804: For low FPS experiments.
            setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
        }
    

    这个looper就是主线程的looper,后续会来一章Choreographer创建的流程追踪。至此刷新UI的入口流程就完成了。

    相关文章

      网友评论

          本文标题:Android UI刷新Framwork层流程

          本文链接:https://www.haomeiwen.com/subject/zjdbvctx.html