這個(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(丟幀)
以時(shí)間的順序來(lái)看下將會(huì)發(fā)生的過(guò)程:
- Display顯示第0幀數(shù)據(jù),此時(shí)CPU和GPU渲染第1幀畫面扁位,且在Display顯示下一幀前完成
- 因?yàn)殇秩炯皶r(shí)准潭,Display在第0幀顯示完成后,也就是第1個(gè)VSync后域仇,緩存進(jìn)行交換刑然,然后正常顯示第1幀
- 接著第2幀開(kāi)始處理,是直到第2個(gè)VSync快來(lái)前才開(kāi)始處理的殉簸。
- 第2個(gè)VSync來(lái)時(shí)闰集,由于第2幀數(shù)據(jù)還沒(méi)有準(zhǔn)備就緒沽讹,緩存沒(méi)有交換,顯示的還是第1幀武鲁。這種情況被Android開(kāi)發(fā)組命名為“Jank”爽雄,即發(fā)生了丟幀。
- 當(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。如下圖:
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呢悴能?如下圖:
- 在第二個(gè)時(shí)間段內(nèi)揣钦,但卻因 GPU 還在處理 B 幀,緩存沒(méi)能交換漠酿,導(dǎo)致 A 幀被重復(fù)顯示冯凹。
- 而B完成后,又因?yàn)槿狈Sync pulse信號(hào),它只能等待下一個(gè)signal的來(lái)臨宇姚。于是在這一過(guò)程中匈庭,有一大段時(shí)間是被浪費(fèi)的。
- 當(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)存。
- 第一個(gè)Jank份帐,是不可避免的璃吧。但是在第二個(gè) 16ms 時(shí)間段,CPU/GPU 使用 第三個(gè) Buffer 完成C幀的計(jì)算废境,雖然還是 會(huì)多顯示一次 A 幀畜挨,但后續(xù)顯示就比較順暢了,有效避免 Jank 的進(jìn)一步加劇噩凹。
- 注意在第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ù)制代碼
- postSyncBarrier 開(kāi)啟同步屏障椎咧,保證VSync到來(lái)后立即執(zhí)行繪制
- 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ù)更新中...