【面試專題】Android屏幕刷新機(jī)制

這個(gè)問(wèn)題在其他人整理的面試寶典中也有提及,一般來(lái)說(shuō)都是問(wèn)View的刷新,基本上從ViewRootImpl的scheduleTraversals()方法開(kāi)始講就可以了甸赃。之前看別人面試斗魚的面經(jīng)惧所,被問(wèn)到了Android屏幕刷新機(jī)制、雙緩沖队萤、三緩沖、黃油計(jì)劃矫钓,然后我面網(wǎng)易云的時(shí)候也確實(shí)被問(wèn)到了這個(gè)題目要尔。

屏幕刷新這一整套舍杜,你把我這篇文章里的內(nèi)容講清楚了,肯定ok了赵辕。網(wǎng)易云還附加問(wèn)了我CPU和GPU怎么交換繪制數(shù)據(jù)的既绩,這個(gè)我個(gè)人認(rèn)為完全是加分題了,我答不出來(lái)还惠,感興趣的小伙伴可以去看一看饲握,你要是能說(shuō)清楚,肯定能讓面試官眼前一亮蚕键。

雙緩沖

在講雙緩沖這個(gè)概念之前救欧,先來(lái)了解一些基礎(chǔ)知識(shí)。

顯示系統(tǒng)基礎(chǔ)

在一個(gè)典型的顯示系統(tǒng)中锣光,一般包括CPU笆怠、GPU、Display三個(gè)部分誊爹, CPU負(fù)責(zé)計(jì)算幀數(shù)據(jù)蹬刷,把計(jì)算好的數(shù)據(jù)交給GPU, GPU會(huì)對(duì)圖形數(shù)據(jù)進(jìn)行渲染频丘,渲染好后放到buffer(圖像緩沖區(qū))里存起來(lái)办成,然后Display(屏幕或顯示器)負(fù)責(zé)把buffer里的數(shù) 據(jù)呈現(xiàn)到屏幕上。

  • 畫面撕裂

屏幕刷新頻是固定的椎镣,比如每16.6ms從buffer取數(shù)據(jù)顯示完一幀诈火,理想情況下幀率和刷新頻率保持一致,即每繪制完成一 幀状答,顯示器顯示一幀冷守。但是CPU/GPU寫數(shù)據(jù)是不可控的,所以會(huì)出現(xiàn)buffer里有些數(shù)據(jù)根本沒(méi)顯示出來(lái)就被重寫了惊科,即 buffer里的數(shù)據(jù)可能是來(lái)自不同的幀的拍摇。當(dāng)屏幕刷新時(shí),此時(shí)它并不知道buffer的狀態(tài)馆截,因此從buffer抓取的幀并不是完整的一幀畫面充活,即出現(xiàn)畫面撕裂。

簡(jiǎn)單說(shuō)就是Display在顯示的過(guò)程中蜡娶,buffer內(nèi)數(shù)據(jù)被CPU/GPU修改混卵,導(dǎo)致畫面撕裂。

那咋解決畫面撕裂呢? 答案是使用雙緩沖窖张。

雙緩沖

由于圖像繪制和屏幕讀取 使用的是同個(gè)buffer幕随,所以屏幕刷新時(shí)可能讀取到的是不完整的一幀畫面。

雙緩沖宿接,讓繪制和顯示器擁有各自的buffer:GPU 始終將完成的一幀圖像數(shù)據(jù)寫入到 Back Buffer赘淮,而顯示器使用 Frame Buffer辕录,當(dāng)屏幕刷新時(shí),F(xiàn)rame Buffer 并不會(huì)發(fā)生變化梢卸,當(dāng)Back buffer準(zhǔn)備就緒后走诞,它們才進(jìn)行交換。

VSync

什么時(shí)候進(jìn)行兩個(gè)buffer的交換呢?

假如是 Back buffer準(zhǔn)備完成一幀數(shù)據(jù)以后就進(jìn)行蛤高,那么如果此時(shí)屏幕還沒(méi)有完整顯示上一幀內(nèi)容的話蚣旱,肯定是會(huì)出問(wèn)題的。 看來(lái)只能是等到屏幕處理完一幀數(shù)據(jù)后戴陡,才可以執(zhí)行這一操作了姻锁。

當(dāng)掃描完一個(gè)屏幕后,設(shè)備需要重新回到第一行以進(jìn)入下一次的循環(huán)猜欺,此時(shí)有一段時(shí)間空隙,稱為VerticalBlanking Interval(VBI)拷窜。這個(gè)時(shí)間點(diǎn)就是我們進(jìn)行緩沖區(qū)交換的最佳時(shí)間开皿。因?yàn)榇藭r(shí)屏幕沒(méi)有在刷新,也就避免了交換過(guò)程中出現(xiàn)畫面撕裂的狀況篮昧。

VSync(垂直同步)是VerticalSynchronization的簡(jiǎn)寫赋荆,它利用VBI時(shí)期出現(xiàn)的vertical sync pulse(垂直同步脈沖)來(lái)保證雙緩沖在最佳時(shí)間點(diǎn)才進(jìn)行交換。另外懊昨,交換是指各自的內(nèi)存地址窄潭,可以認(rèn)為該操作是瞬間完成。

所以說(shuō)VSync這個(gè)概念并不是Google首創(chuàng)的酵颁,它在早年的PC機(jī)領(lǐng)域就已經(jīng)出現(xiàn)了嫉你。

Android屏幕刷新機(jī)制

先總體概括一下,Android屏幕刷新使用的是“雙緩存+VSync機(jī)制”躏惋,單純的雙緩沖模式容易造成jank(丟幀)現(xiàn)象幽污,為了解決這個(gè)問(wèn)題,Google在 Android4.1 提出了Project Butter(?油工程)簿姨,引入了 drawing with VSync 的概念距误。

jank(丟幀)

VSync.jpeg

以時(shí)間的順序來(lái)看下將會(huì)發(fā)生的過(guò)程:

  1. Display顯示第0幀數(shù)據(jù),此時(shí)CPU和GPU渲染第1幀畫面扁位,且在Display顯示下一幀前完成
  2. 因?yàn)殇秩炯皶r(shí)准潭,Display在第0幀顯示完成后,也就是第1個(gè)VSync后域仇,緩存進(jìn)行交換刑然,然后正常顯示第1幀
  3. 接著第2幀開(kāi)始處理,是直到第2個(gè)VSync快來(lái)前才開(kāi)始處理的殉簸。
  4. 第2個(gè)VSync來(lái)時(shí)闰集,由于第2幀數(shù)據(jù)還沒(méi)有準(zhǔn)備就緒沽讹,緩存沒(méi)有交換,顯示的還是第1幀武鲁。這種情況被Android開(kāi)發(fā)組命名為“Jank”爽雄,即發(fā)生了丟幀。
  5. 當(dāng)?shù)?幀數(shù)據(jù)準(zhǔn)備完成后沐鼠,它并不會(huì)?上被顯示挚瘟,而是要等待下一個(gè)VSync 進(jìn)行緩存交換再顯示。

所以總的來(lái)說(shuō)饲梭,就是屏幕平白無(wú)故地多顯示了一次第1幀乘盖。 原因是第2幀的CPU/GPU計(jì)算 沒(méi)能在VSync信號(hào)到來(lái)前完成。

這里注意一下一個(gè)細(xì)節(jié)憔涉,jank(丟幀订框、掉幀),不是說(shuō)這一幀丟棄了不顯示兜叨,而是這一幀延遲顯示了穿扳,因?yàn)榫彺娼粨Q的時(shí)機(jī)只能等下一個(gè)VSync了。

黃油計(jì)劃 —— drawing with VSync

為了優(yōu)化顯示性能国旷,Google在Android 4.1系統(tǒng)中對(duì)Android Display系統(tǒng)進(jìn)行了重構(gòu)矛物,實(shí)現(xiàn)了Project Butter(?油工程): 系統(tǒng)在收到VSync pulse后,將?上開(kāi)始下一幀的渲染跪但。即一旦收到VSync通知(16ms觸發(fā)一次)履羞,CPU和GPU 才立刻開(kāi) 始計(jì)算然后把數(shù)據(jù)寫入buffer。如下圖:

VSync2.jpeg

CPU/GPU根據(jù)VSYNC信號(hào)同步處理數(shù)據(jù)屡久,可以讓CPU/GPU有完整的16ms時(shí)間來(lái)處理數(shù)據(jù)忆首,減少了jank。 一句話總結(jié)涂身,VSync同步使得CPU/GPU充分利用了16.6ms時(shí)間雄卷,減少jank。

問(wèn)題又來(lái)了蛤售,如果界面比較復(fù)雜丁鹉,CPU/GPU的處理時(shí)間較?,超過(guò)了16.6ms呢悴能?如下圖:

VSync3.jpeg
  1. 在第二個(gè)時(shí)間段內(nèi)揣钦,但卻因 GPU 還在處理 B 幀,緩存沒(méi)能交換漠酿,導(dǎo)致 A 幀被重復(fù)顯示冯凹。
  2. 而B完成后,又因?yàn)槿狈Sync pulse信號(hào),它只能等待下一個(gè)signal的來(lái)臨宇姚。于是在這一過(guò)程中匈庭,有一大段時(shí)間是被浪費(fèi)的。
  3. 當(dāng)下一個(gè)VSync出現(xiàn)時(shí)浑劳,CPU/GPU?上執(zhí)行操作(A幀)阱持,且緩存交換,相應(yīng)的顯示屏對(duì)應(yīng)的就是B魔熏。這時(shí)看起來(lái)就是正常的衷咽。只不過(guò)由于執(zhí)行時(shí)間仍然超過(guò)16ms,導(dǎo)致下一次應(yīng)該執(zhí)行的緩沖區(qū)交換又被推遲了——如此循環(huán)反復(fù)蒜绽,便出現(xiàn)了越來(lái)越多的“Jank”镶骗。

為什么 CPU 不能在第二個(gè) 16ms 處理繪制工作呢? 因?yàn)橹挥袃蓚€(gè) buffer躲雅,Back buffer正在被GPU用來(lái)處理B幀的數(shù)據(jù)鼎姊, Frame buffer的內(nèi)容用于Display的顯示,這樣兩個(gè) buffer都被占用相赁,CPU 則無(wú)法準(zhǔn)備下一幀的數(shù)據(jù)此蜈。 那么,如果再提供一個(gè)buffer噪生,CPU冲杀、GPU 和顯示設(shè)備都能使用各自的 buffer工作抱怔,互不影響。這就是三緩沖的來(lái)源了砍聊。

三緩沖

三緩存就是在雙緩沖機(jī)制基礎(chǔ)上增加了一個(gè) Graphic Buffer 緩沖區(qū)页藻,這樣可以最大限度的利用空閑時(shí)間桨嫁,帶來(lái)的壞處是多使用的一個(gè) Graphic Buffer 所占用的內(nèi)存。

VSync4.jpeg
  1. 第一個(gè)Jank份帐,是不可避免的璃吧。但是在第二個(gè) 16ms 時(shí)間段,CPU/GPU 使用 第三個(gè) Buffer 完成C幀的計(jì)算废境,雖然還是 會(huì)多顯示一次 A 幀畜挨,但后續(xù)顯示就比較順暢了,有效避免 Jank 的進(jìn)一步加劇噩凹。
  2. 注意在第3段中巴元,A幀的計(jì)算已完成,但是在第4個(gè)vsync來(lái)的時(shí)候才顯示驮宴,如果是雙緩沖逮刨,那在第三個(gè)vynsc就可以顯示了。

三緩沖有效利用了等待VSync的時(shí)間堵泽,減少了jank修己,但是帶來(lái)了延遲恢总。是不是 Buffer 越多越好呢?這個(gè)是否定的睬愤, Buffer 正常還是兩個(gè)片仿,當(dāng)出現(xiàn) Jank 后三個(gè)足以。

Choreographer

上邊講的都是基礎(chǔ)的刷新知識(shí)戴涝,那么在 Android 系統(tǒng)中滋戳,真正來(lái)實(shí)現(xiàn)繪制的類叫Choreographer

Choreographer負(fù)責(zé)對(duì)CPU/GPU繪制的指導(dǎo) —— 收到VSync信號(hào)才開(kāi)始繪制啥刻,保證繪制擁有完整 16.6ms奸鸯,避免繪制的隨機(jī)性。

通常 應(yīng)用層不會(huì)直接使用Choreographer可帽,而是使用更高級(jí)的API娄涩,例如動(dòng)畫和View繪制相關(guān)的 ValueAnimator.start()、View.invalidate()等映跟。

(這邊補(bǔ)充說(shuō)一個(gè)面試題蓄拣,屬性動(dòng)畫更新時(shí)會(huì)回調(diào)onDraw嗎?不會(huì)努隙,因?yàn)樗鼉?nèi)部是通過(guò)AnimationHandler中的Choreographer機(jī)制來(lái)實(shí)現(xiàn)的更新球恤,具體的邏輯,如果以后有時(shí)間的話可以寫篇文章來(lái)說(shuō)一說(shuō)荸镊。)

業(yè)界一般通過(guò)Choreographer來(lái)監(jiān)控應(yīng)用的幀率咽斧。

(這個(gè)東西也是個(gè)面試題,會(huì)問(wèn)你如何檢測(cè)應(yīng)用的幀率躬存?你可以提一下Choreographer里面的FrameCallback张惹,然后結(jié)合一些第三方庫(kù)的實(shí)現(xiàn)具體說(shuō)一下。)

View刷新的入口

Activity啟動(dòng)岭洲,走完onResume方法后宛逗,會(huì)進(jìn)行window的添加。window添加過(guò)程會(huì)調(diào)用ViewRootImpl的setView()方法盾剩, setView()方法會(huì)調(diào)用requestLayout()方法來(lái)請(qǐng)求繪制布局雷激,requestLayout()方法內(nèi)部又會(huì)走到scheduleTraversals()方法。最后會(huì)走到performTraversals()方法告私,接著到了我們熟知的測(cè)量侥锦、布局、繪制三大流程了德挣。

當(dāng)我們使用 ValueAnimator.start()恭垦、View.invalidate()時(shí),最后也是走到ViewRootImpl的 scheduleTraversals()方法。(View.invalidate()內(nèi)部會(huì)循環(huán)獲取ViewParent直到ViewRootImpl的invalidateChildInParent()方法番挺,然后走到scheduleTraversals()唠帝,可自行查看源碼)

即所有UI的變化都是走到ViewRootImpl的scheduleTraversals()方法。

這里注意一個(gè)點(diǎn):scheduleTraversals()之后不是立即就執(zhí)行performTraversals()的玄柏,它們中間隔了一個(gè)Choreographer機(jī)制襟衰。簡(jiǎn)單來(lái)說(shuō)就是scheduleTraversals()中,Choreographer會(huì)去請(qǐng)求native的VSync信號(hào)粪摘,VSync信號(hào)來(lái)了之后才會(huì)去調(diào)用performTraversals()方法進(jìn)行View繪制的三大流程瀑晒。


 //ViewRootImpl.java
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //添加同步屏障,屏蔽同步消息徘意,保證VSync到來(lái)立即執(zhí)行繪制
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); 
        //mTraversalRunnable是TraversalRunnable實(shí)例苔悦,最終走到run(),也即doTraversal();
        mChoreographer.postCallback(
                      Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
         doTraversal();
    } 
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        //移除同步屏障 
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); ...
        //開(kāi)始三大繪制流程
        performTraversals();
        ...
    } 
}
復(fù)制代碼
  1. postSyncBarrier 開(kāi)啟同步屏障椎咧,保證VSync到來(lái)后立即執(zhí)行繪制
  2. mChoreographer.postCallback()方法玖详,發(fā)送一個(gè)會(huì)在下一幀執(zhí)行的回調(diào),即在下一個(gè)VSync到來(lái)時(shí)會(huì)執(zhí)行 TraversalRunnable–>doTraversal()—>performTraversals()–>繪制流程勤讽。

Choreographer

初始化

mChoreographer蟋座,是在ViewRootImpl的構(gòu)造方法內(nèi)使用 Choreographer.getInstance()創(chuàng)建。

Choreographer和Looper一樣是線程單例的脚牍,通過(guò)ThreadLocal機(jī)制來(lái)保證唯一性向臀。因?yàn)镃horeographer內(nèi)部通過(guò)FrameHandler來(lái)發(fā)送消息,所以初始化的時(shí)候會(huì)先判斷當(dāng)前線程有無(wú)Looper诸狭,沒(méi)有的話直接拋異常飒硅。

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) {
         //當(dāng)前線程要有l(wèi)ooper,Choreographer實(shí)例需要傳入
              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;
   }
};
復(fù)制代碼

postCallback

mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)方法作谚,第一個(gè)參數(shù)是CALLBACK_TRAVERSAL,表示回調(diào)任務(wù)的類型庵芭,共有以下5種類型:

//輸入事件妹懒,首先執(zhí)行
public static final int CALLBACK_INPUT = 0; 
//動(dòng)畫,第二執(zhí)行
public static final int CALLBACK_ANIMATION = 1; 
//插入更新的動(dòng)畫双吆,第三執(zhí)行
public static final int CALLBACK_INSETS_ANIMATION = 2; 
//繪制眨唬,第四執(zhí)行
public static final int CALLBACK_TRAVERSAL = 3; 
//提交,最后執(zhí)行好乐,
public static final int CALLBACK_COMMIT = 4;
復(fù)制代碼

五種類型任務(wù)對(duì)應(yīng)存入對(duì)應(yīng)的CallbackQueue中匾竿,每當(dāng)收到 VSYNC 信號(hào)時(shí),Choreographer 將首先處理 INPUT 類型的任 務(wù)蔚万,然后是 ANIMATION 類型岭妖,最后才是 TRAVERSAL 類型。

postCallback()內(nèi)部調(diào)用postCallbackDelayed(),接著又調(diào)用postCallbackDelayedInternal()昵慌,正常消息執(zhí)行scheduleFrameLocked假夺,延遲運(yùn)行的消息會(huì)發(fā)送一個(gè)MSG_DO_SCHEDULE_CALLBACK類型的meessage:

private void postCallbackDelayedInternal(int callbackType,
      Object action, Object token, long delayMillis) {
    ...
    synchronized (mLock) {
        ...
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
        if (dueTime <= now) { //立即執(zhí)行
             scheduleFrameLocked(now);
        } else {
            //延遲運(yùn)行,最終也會(huì)走到scheduleFrameLocked()
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); 
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        } 
    }
}
復(fù)制代碼

FrameHandler這個(gè)類是內(nèi)部專門用來(lái)處理消息的斋攀,可以看到延遲的MSG_DO_SCHEDULE_CALLBACK類型消息最終也是走到scheduleFrameLocked:

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:
                // 執(zhí)行doFrame,即繪制過(guò)程 
                doFrame(System.nanoTime(), 0);
                break;
            case MSG_DO_SCHEDULE_VSYNC: 
                //申請(qǐng)VSYNC信號(hào)已卷,例如當(dāng)前需要繪制任務(wù)時(shí) 
                doScheduleVsync();
                break;
            case MSG_DO_SCHEDULE_CALLBACK: 
                //需要延遲的任務(wù),最終還是執(zhí)行上述兩個(gè)事件 
                doScheduleCallback(msg.arg1);
                break;
        } 
    }
}

void doScheduleCallback(int callbackType) {
    synchronized (mLock) {
        if (!mFrameScheduled) {
            final long now = SystemClock.uptimeMillis();
            if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                scheduleFrameLocked(now);
            }
        } 
    }
}
復(fù)制代碼

申請(qǐng)VSync信號(hào)

scheduleFrameLocked()方法里面就會(huì)去真正的申請(qǐng) VSync 信號(hào)了淳蔼。

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true; 
        if (USE_VSYNC) {
            //當(dāng)前執(zhí)行的線程侧蘸,是否是mLooper所在線程
            if (isRunningOnLooperThreadLocked()) {
                //申請(qǐng) VSYNC 信號(hào)
                scheduleVsyncLocked();
            } else {
                // 若不在,就用mHandler發(fā)送消息到原線程鹉梨,最后還是調(diào)用scheduleVsyncLocked方法 
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); 
                msg.setAsynchronous(true);//異步 
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } else {
            // 如果未開(kāi)啟VSYNC則直接doFrame方法(4.1后默認(rèn)開(kāi)啟) 
            final long nextFrameTime = Math.max(
            mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
            Message msg = mHandler.obtainMessage(MSG_DO_FRAME); 
            msg.setAsynchronous(true);//異步 
            mHandler.sendMessageAtTime(msg, nextFrameTime);
        } 
    }
}
復(fù)制代碼

VSync信號(hào)的注冊(cè)和監(jiān)聽(tīng)是通過(guò)mDisplayEventReceiver實(shí)現(xiàn)的讳癌。mDisplayEventReceiver是在Choreographer的構(gòu)造方法中創(chuàng)建的,是FrameDisplayEventReceiver的實(shí)例俯画。 FrameDisplayEventReceiver是 DisplayEventReceiver 的子類析桥,

private void scheduleVsyncLocked() {
    mDisplayEventReceiver.scheduleVsync();
}
復(fù)制代碼
public DisplayEventReceiver(Looper looper, int vsyncSource) {
    if (looper == null) {
        throw new IllegalArgumentException("looper must not be null");
    }
    mMessageQueue = looper.getQueue();
    // 注冊(cè)native的VSYNC信號(hào)監(jiān)聽(tīng)者
    mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,vsyncSource);
    mCloseGuard.open("dispose");
}
復(fù)制代碼

VSync信號(hào)回調(diào)

native的VSync信號(hào)到來(lái)時(shí),會(huì)走到onVsync()回調(diào):

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
        implements Runnable {

    @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
        ...
        //將本身作為runnable傳入msg艰垂, 發(fā)消息后 會(huì)走run()泡仗,即doFrame(),也是異步消息 
        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);
    }
}
復(fù)制代碼

(這里補(bǔ)充一個(gè)面試題:頁(yè)面UI沒(méi)有刷新的時(shí)候onVsync()回調(diào)也會(huì)執(zhí)行嗎猜憎?不會(huì)娩怎,因?yàn)閂Sync是UI需要刷新的時(shí)候主動(dòng)去申請(qǐng)的,而不是native層不停地往上面去推這個(gè)回調(diào)的胰柑,這邊要注意截亦。)

doFrame

doFrame()方法中會(huì)通過(guò)doCallbacks()方法去執(zhí)行各種callbacks,主要內(nèi)容就是取對(duì)應(yīng)任務(wù)類型的隊(duì)列柬讨,遍歷隊(duì)列執(zhí)行所有任務(wù)崩瓤,其中就包括了 ViewRootImpl 發(fā)起的繪制任務(wù)mTraversalRunnable了。mTraversalRunnable執(zhí)行doTraversal()方法踩官,移除同步屏障却桶,調(diào)用performTraversals()開(kāi)始三大繪制流程。

到這里整個(gè)流程就閉環(huán)了蔗牡。

本文在開(kāi)源項(xiàng)目:https://github.com/Android-Alvin/Android-LearningNotes 中已收錄颖系,里面包含了Android組件化最全開(kāi)源項(xiàng)目(美團(tuán)App、得到App辩越、支付寶App嘁扼、微信App、蘑菇街App黔攒、有贊APP...)等趁啸,資源持續(xù)更新中...

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末强缘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子莲绰,更是在濱河造成了極大的恐慌欺旧,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛤签,死亡現(xiàn)場(chǎng)離奇詭異辞友,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)震肮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門称龙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人戳晌,你說(shuō)我怎么就攤上這事鲫尊。” “怎么了沦偎?”我有些...
    開(kāi)封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵疫向,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我豪嚎,道長(zhǎng)搔驼,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任侈询,我火速辦了婚禮舌涨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扔字。我一直安慰自己囊嘉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布革为。 她就那樣靜靜地躺著扭粱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪震檩。 梳的紋絲不亂的頭發(fā)上琢蛤,一...
    開(kāi)封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音恳蹲,去河邊找鬼。 笑死俩滥,一個(gè)胖子當(dāng)著我的面吹牛嘉蕾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播霜旧,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼错忱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼儡率!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起以清,我...
    開(kāi)封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤儿普,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后掷倔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體眉孩,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年勒葱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浪汪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凛虽,死狀恐怖死遭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凯旋,我是刑警寧澤呀潭,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站至非,受9級(jí)特大地震影響钠署,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睡蟋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一踏幻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧戳杀,春花似錦该面、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至傍菇,卻和暖如春猾瘸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丢习。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工牵触, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咐低。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓揽思,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親见擦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钉汗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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