深入了解Android View 绘制流程

作者: gdutkyle | 来源:发表于2020-03-24 23:02 被阅读0次

    View的绘制还有什么好聊的

    前面我们已经减少了view的绘制流程了,很多同学都知道,面试的时候面试官问你:view的绘制流程是什么?绝大部分同学都会脱口而出:onMeasure()->onLayout()-onDraw(),然后把一切的流程给介绍出来,就像我上一篇文章所写的Android View 绘制流程。但是按照现在的android就业形势,如果只会这些“表面”的东西难免会让人缺少眼前一亮的感觉。那么今天我们就深入的了解一下完整的View绘制

    谁调用了performTraversals()?

    我们知道onMeasure、onLayout、onDraw这几个方法对应的就是ViewRootImp.java里面的performMeasure、performLayout、performDraw方法。这几个方法的入口就是performTraversals()。那么谁调用了performTraversals()方法呢?我们一步一步往下跟。

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

    performTraversals()方法由doTraversal方法调用

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

    doTraversal()方法是由TraversalRunnable方法调用。那么我们只需要看到谁调用了mTraversalRunnable即可

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

    好了,调用链很明确了,就是scheduleTraversal方法触发了View的绘制流程。这个方法有很多的调用地方,最经典的就是View的invalidate()方法了。

    View的invalidate流程分析

     void invalidate() {
            mDirty.set(0, 0, mWidth, mHeight);
            if (!mWillDrawSoon) {
                scheduleTraversals();
            }
        }
    

    重中之重scheduleTraversals()

    我们看一下scheduleTraversal方法的调用

      mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    
    

    这个方法非常的重要。Choreographer的作用就是协调input时间、动画和绘制的时间。其实如果大家知道vsync的话,那么我们就可以说,choreography就是监听vsync的心跳时间的callback,vsync会每隔16.7ms回调一次。我们看choreography源码

    private void postCallbackDelayedInternal(int callbackType,
                Object action, Object token, long delayMillis) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "PostCallback: type=" + callbackType
                        + ", action=" + action + ", token=" + token
                        + ", delayMillis=" + 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);
                }
            }
        }
    

    我们可以看到发送了一个MSG_DO_SCHEDULE_CALLBACK的消息,再继续往下跟

    private final class FrameHandler extends Handler {
            public FrameHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_DO_FRAME:
                        doFrame(System.nanoTime(), 0);
                        break;
                    case MSG_DO_SCHEDULE_VSYNC:
                        doScheduleVsync();
                        break;
                    case MSG_DO_SCHEDULE_CALLBACK:
                        doScheduleCallback(msg.arg1);
                        break;
                }
            }
        }
    

    这里终于可以看到所有的消息的处理了,也包括前面提到的vsync流程

    /**
         * Schedules a single vertical sync pulse to be delivered when the next
         * display frame begins.
         */
        public void scheduleVsync() {
            if (mReceiverPtr == 0) {
                Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                        + "receiver has already been disposed.");
            } else {
                nativeScheduleVsync(mReceiverPtr);
            }
        }
    
    

    这里就是调用native方法去触发vsync了。我们重点看下doFrame()方法

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

    这里也就符合我们开头所说的:Choreography方法是协调input、动画、和绘制流程的管理者。这里我们可以做一个引申,为什么android系统可以有动画,每一个动画的执行粒度和区间值是怎么保存和触发的,其实都是跟这个vsync相关联。

     mFrameInfo.markPerformTraversalsStart();
                doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    

    这里就回调会ViewRootImp.java里面的TraversalRunnable方法中

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

    这里就开始和前面的分析流程形成了闭环。
    自此上层的View绘制流程就分析完毕了。关于Vsync我们会在接下来的文章继续做分析,待更~~

    相关文章

      网友评论

        本文标题:深入了解Android View 绘制流程

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