深入了解Android View 繪制流程

View的繪制還有什么好聊的

前面我們已經(jīng)減少了view的繪制流程了葱跋,很多同學都知道娱俺,面試的時候面試官問你:view的繪制流程是什么废麻?絕大部分同學都會脫口而出:onMeasure()->onLayout()-onDraw()脑溢,然后把一切的流程給介紹出來,就像我上一篇文章所寫的Android View 繪制流程屑彻。但是按照現(xiàn)在的android就業(yè)形勢验庙,如果只會這些“表面”的東西難免會讓人缺少眼前一亮的感覺。那么今天我們就深入的了解一下完整的View繪制

誰調(diào)用了performTraversals()社牲?

我們知道onMeasure粪薛、onLayout、onDraw這幾個方法對應的就是ViewRootImp.java里面的performMeasure搏恤、performLayout违寿、performDraw方法。這幾個方法的入口就是performTraversals()熟空。那么誰調(diào)用了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方法調(diào)用

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

doTraversal()方法是由TraversalRunnable方法調(diào)用息罗。那么我們只需要看到誰調(diào)用了mTraversalRunnable即可

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

好了,調(diào)用鏈很明確了,就是scheduleTraversal方法觸發(fā)了View的繪制流程孩革。這個方法有很多的調(diào)用地方锅移,最經(jīng)典的就是View的invalidate()方法了。

View的invalidate流程分析

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

重中之重scheduleTraversals()

我們看一下scheduleTraversal方法的調(diào)用

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

這個方法非常的重要坤学。Choreographer的作用就是協(xié)調(diào)input時間、動畫和繪制的時間杂穷。其實如果大家知道vsync的話,那么我們就可以說趴拧,choreography就是監(jiān)聽vsync的心跳時間的callback兄渺,vsync會每隔16.7ms回調(diào)一次瞎饲。我們看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);
            }
        }
    }

我們可以看到發(fā)送了一個MSG_DO_SCHEDULE_CALLBACK的消息疟呐,再繼續(xù)往下跟

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

這里就是調(diào)用native方法去觸發(fā)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方法是協(xié)調(diào)input、動畫、和繪制流程的管理者塘秦。這里我們可以做一個引申,為什么android系統(tǒng)可以有動畫菱皆,每一個動畫的執(zhí)行粒度和區(qū)間值是怎么保存和觸發(fā)的须误,其實都是跟這個vsync相關聯(lián)。

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

這里就回調(diào)會ViewRootImp.java里面的TraversalRunnable方法中

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

這里就開始和前面的分析流程形成了閉環(huán)篷店。
自此上層的View繪制流程就分析完畢了疲陕。關于Vsync我們會在接下來的文章繼續(xù)做分析携茂,待更~~

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市卿堂,隨后出現(xiàn)的幾起案子穗慕,更是在濱河造成了極大的恐慌瓢对,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凯正,死亡現(xiàn)場離奇詭異毙玻,居然都是意外死亡,警方通過查閱死者的電腦和手機廊散,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門桑滩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人允睹,你說我怎么就攤上這事运准』系” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵胁澳,是天一觀的道長该互。 經(jīng)常有香客問我,道長韭畸,這世上最難降的妖魔是什么宇智? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮胰丁,結果婚禮上随橘,老公的妹妹穿的比我還像新娘。我一直安慰自己锦庸,他們只是感情好机蔗,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甘萧,像睡著了一般蜒车。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上幔嗦,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音沥潭,去河邊找鬼邀泉。 笑死,一個胖子當著我的面吹牛钝鸽,可吹牛的內(nèi)容都是我干的汇恤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拔恰,長吁一口氣:“原來是場噩夢啊……” “哼因谎!你這毒婦竟也來了?” 一聲冷哼從身側響起颜懊,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤财岔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后河爹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匠璧,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年咸这,在試婚紗的時候發(fā)現(xiàn)自己被綠了夷恍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡媳维,死狀恐怖酿雪,靈堂內(nèi)的尸體忽然破棺而出遏暴,到底是詐尸還是另有隱情,我是刑警寧澤指黎,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布朋凉,位于F島的核電站,受9級特大地震影響袋励,放射性物質(zhì)發(fā)生泄漏侥啤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一茬故、第九天 我趴在偏房一處隱蔽的房頂上張望盖灸。 院中可真熱鬧,春花似錦磺芭、人聲如沸赁炎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽徙垫。三九已至,卻和暖如春放棒,著一層夾襖步出監(jiān)牢的瞬間姻报,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工间螟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吴旋,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓厢破,卻偏偏與公主長得像荣瑟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子摩泪,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容