十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
前言
我們提供的服務(wù)有:網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、平鄉(xiāng)ssl等。為上1000家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的平鄉(xiāng)網(wǎng)站制作公司
隨著App的開發(fā)到了某個(gè)階段必然會(huì)遇到一個(gè)需求,那就是優(yōu)化頁(yè)面的啟動(dòng)時(shí)間。
第一個(gè)問題:有什么方法可以去統(tǒng)計(jì)頁(yè)面的啟動(dòng)時(shí)間呢?
adb?logcat?-s?ActivityManager?|?grep?"Displayed"
上面的命令行可用來進(jìn)行查看。
第二個(gè)問題:?jiǎn)?dòng)時(shí)間是包括了哪些流程,是如何被計(jì)算出來的呢?
App啟動(dòng)主要經(jīng)過如下幾個(gè)流程
Launch the process.
Initialize the objects.
Create and initialize the activity.
Inflate the layout.
Draw your application for the first time.
最末尾的步驟5是繪制你的界面。所以完整的啟動(dòng)時(shí)間是要到繪制完成為止。
那么繪制界面對(duì)應(yīng)的是什么時(shí)候呢?一般我們開發(fā),最晚能被回調(diào)的是在onResume方法,那么onResume方法是在繪制之后還是之前呢?
no code no truth
?final?void?handleResumeActivity(IBinder?token,?boolean?clearHide,?boolean?isForward,?boolean?reallyResume,?int?seq,?String?reason)?{?//省略部分代碼 ?r?=?performResumeActivity(token,?clearHide,?reason);?//省略部分代碼 ?if?(a.mVisibleFromClient)?{?if?(!a.mWindowAdded)?{ ?a.mWindowAdded?=?true; ?wm.addView(decor,?l);
看上面的代碼,就先放結(jié)論了。
在performResumeActivity 中進(jìn)行了onResume的回調(diào),在wm.addView 中進(jìn)行了繪制,因此onResume的方法是在繪制之前,在onResume中做一些耗時(shí)操作都會(huì)影響啟動(dòng)時(shí)間。
下面就剝一下onResume的邏輯,繪制的有興趣可以自己看源碼。 首先performResumeActivity中會(huì)調(diào)用r.activity.performResume();
?public?final?ActivityClientRecord?performResumeActivity(IBinder?token,?boolean?clearHide,?String?reason)?{?//省略部分代碼 ?try?{ ?r.activity.onStateNotSaved(); ?r.activity.mFragments.noteStateNotSaved(); ?checkAndBlockForNetworkAccess();?if?(r.pendingIntents?!=?null)?{ ?deliverNewIntents(r,?r.pendingIntents); ?r.pendingIntents?=?null; ?}?if?(r.pendingResults?!=?null)?{ ?deliverResults(r,?r.pendingResults); ?r.pendingResults?=?null; ?} ?r.activity.performResume();?//省略部分代碼 ?} ?}
然后在performResume中調(diào)用了 mInstrumentation.callActivityOnResume(this);
?final?void?performResume()?{?//省略部分代碼 ?mInstrumentation.callActivityOnResume(this);?//省略部分代碼 ?}
最后在callActivityOnResume 調(diào)用了onResume
?public?void?callActivityOnResume(Activity?activity)?{ ?activity.mResumed?=?true; ?activity.onResume();?//省略代碼 ?}
到了此處就算真正調(diào)用到了onResume的方法。
既然知道了onResume中做的操作會(huì)影響到啟動(dòng)時(shí)間,那么就有一個(gè)優(yōu)化啟動(dòng)時(shí)間的思路了。
思路
把在onResume以及其之前的調(diào)用的但非必須的事件(如某些界面View的繪制)挪出來找一個(gè)時(shí)機(jī)(即繪制完成以后)去調(diào)用。那樣啟動(dòng)時(shí)間自然就縮短了。但是整體做的事并沒有明顯變化。那么這個(gè)時(shí)機(jī)是什么呢?
IdleHandler
看下IdleHandler的源碼
?/** ?*?Callback?interface?for?discovering?when?a?thread?is?going?to?block ?*?waiting?for?more?messages. ?*/ ?public?static?interface?IdleHandler?{?/** ?*?Called?when?the?message?queue?has?run?out?of?messages?and?will?now ?*?wait?for?more.?Return?true?to?keep?your?idle?handler?active,?false ?*?to?have?it?removed.?This?may?be?called?if?there?are?still?messages ?*?pending?in?the?queue,?but?they?are?all?scheduled?to?be?dispatched ?*?after?the?current?time. ?*/ ?boolean?queueIdle(); ?}
從這個(gè)源碼可知道,IdleHandler即在looper里面的message處理完了的時(shí)候去調(diào)用,這不就是我們onResume調(diào)用完了以后的時(shí)機(jī)么。
來一張圖說明一下,明顯的IdleHandler在onResume以及performTraversals繪制之后調(diào)用
由這個(gè)思路我把自己負(fù)責(zé)的頁(yè)面中的一些界面的繪制邏輯挪到了IdleHandler中,由于有LoadingView時(shí)間,我把Adapter的綁定也挪出去了??聪聝?yōu)化前后效果圖 ,效果還是挺明顯的;