android 延遲加載

轉載:http://androidperformance.com/2015/11/18/Android-app-lunch-optimize-delay-load.html#footer

應用啟動優(yōu)化概述

在 Android 開發(fā)中,應用啟動速度是一個非常重要的點,應用啟動優(yōu)化也是一個非常重要的過程.對于應用啟動優(yōu)化,其實核心思想就是在啟動過程中少做事情,具體實踐的時候無非就是下面幾種:

1、異步加載

2、延時加載

3煎殷、懶加載

不用一一去解釋,做過啟動優(yōu)化的估計都使用過,本篇文章將詳細講解一下一種延時加載的實現以及其原理.

其實這種加載的實現是非常簡單的,但是其中的原理可能比較復雜,還涉及到Looper/Handler/MessageQueue/VSYNC等.以及其中碰到的一些問題,還會有一些我自己額外的思考.

1. 優(yōu)化后的DelayLoad的實現

一提到DelayLoad,大家可能第一時間想到的就是在 onCreate 里面調用 Handler.postDelayed方法, 將需要 Delay 加載的東西放到這里面去初始化, 這個也是一個比較方便的方法. Delay一段時間再去執(zhí)行,這時候應用已經加載完成,界面已經顯示出來了, 不過這個方法有一個致命的問題: 延遲多久?

大家都知道,在 Android 的高端機型上,應用的啟動是非常快的 , 這時候只需要 Delay 很短的時間就可以了, 但是在低端機型上,應用的啟動就沒有那么快了,而且現在應用為了兼容舊的機型,往往需要 Delay 較長的時間,這樣帶來體驗上的差異是很明顯的.

這里先說優(yōu)化方案:

1奄毡、首先 , 創(chuàng)建 Handler 和 Runnable 對象, 其中 Runnable 對象的 run方法里面去更新 UI 線程.

`privateHandler myHandler =newHandler();

privateRunnable mLoadingRunnable =newRunnable() {

? ? @Override

? ? publicvoidrun(){

? ? ? ? updateText();//更新UI線程

? ? }

};`

在主 Activity 的 onCreate 中加入下面的代碼:

getWindow().getDecorView().post(newRunnable() {

? ? @Override

? ? public void run(){

? ? ? ? myHandler.post(mLoadingRunnable);

? ? }

});

其實實現的話非常簡單,我們來對比一下三種方案的效果.

2. 三種寫法的差異對比

為了驗證我們優(yōu)化的 DelayLoad的效果,我們寫了一個簡單的app , 這個 App 中包含三張不同大小的圖片,每張圖片下面都會有一個 TextView , 來標記圖片的顯示高度和寬度. MainActivity的代碼如下:

publicclassMainActivityextendsAppCompatActivity{

privatestaticfinalintDEALY_TIME =300;

privateImageView imageView1;

privateImageView imageView2;

privateImageView imageView3;

privateTextView textView1;

privateTextView textView2;

privateTextView textView3;

privateHandler myHandler =newHandler();

privateRunnable mLoadingRunnable =newRunnable() {

@Override

publicvoidrun(){

updateText();

}

};

@Override

protectedvoidonCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

imageView1 = (ImageView) findViewById(R.id.image1);

imageView2 = (ImageView) findViewById(R.id.image2);

imageView3 = (ImageView) findViewById(R.id.image3);

textView1 = (TextView) findViewById(R.id.text1);

textView2 = (TextView) findViewById(R.id.text2);

textView3 = (TextView) findViewById(R.id.text3);

//? ? ? 第一種寫法:直接Post

myHandler.post(mLoadingRunnable);

//? ? ? 第二種寫法:直接PostDelay 300ms.

//? ? ? ? myHandler.postDelayed(mLoadingRunnable, DEALY_TIME);

//? ? ? 第三種寫法:優(yōu)化的DelayLoad

//? ? ? getWindow().getDecorView().post(new Runnable() {

//? ? ? ? ? ? @Override

//? ? ? ? ? ? public void run() {

//? ? ? ? ? ? ? ? myHandler.post(mLoadingRunnable);

//? ? ? ? ? ? }

//? ? ? ? });

// Dump當前的MessageQueue信息.

getMainLooper().dump(newPrinter() {

@Override

publicvoidprintln(String x){

Log.i("Gracker",x);

}

},"onCreate");

}

privatevoidupdateText(){

TraceCompat.beginSection("updateText");

textView1.setText("image1 : w="+ imageView1.getWidth() +

" h ="+ imageView1.getHeight());

textView2.setText("image2 : w="+ imageView2.getWidth() +

" h ="+ imageView2.getHeight());

textView3.setText("image3 : w="+ imageView3.getWidth() +

" h ="+ imageView3.getHeight());

TraceCompat.endSection();

}

我們需要關注兩個點:

updateText 這個函數是什么時候被執(zhí)行的?

App 啟動后,三個圖片的長寬是否可以被正確地顯示出來?

是否有 Delay Load 的效果?

2.1 第一種寫法

updateText執(zhí)行的時機?

下面是第一種寫法的Trace圖:

可以看到 updateText 是在 Activity 的 onCreate/onStart/onResume三個回調執(zhí)行完成后才去執(zhí)行的.

圖片的寬高是否正確顯示?

從圖片看一看到,寬高并沒有顯示. 這是為什么呢? 這個問題就要從Activity 的 onCreate/onStart/onResume三個回調說起了. 其實Activity 的 onCreate/onStart/onResume三個回調中,并沒有執(zhí)行Measure和Layout操作, 這個是在后面的performTraversals中才執(zhí)行的. 所以在這之前寬高都是0.

是否有 Delay Load 的效果?

并沒有. 因為我們知道, 應用啟動的時候,要等兩次 performTraversals 都執(zhí)行完成之后才會顯示第一幀, 而 updateText 這個方法在第一個 performTraversals 執(zhí)行之前就執(zhí)行了. 所以 updateText 方法的執(zhí)行時間是算在應用啟動的時間里面的.

2.2 第二種寫法

第二種寫法我們Delay了300ms .我們來看一下表現.

updateText執(zhí)行的時機?

可以看到,這種寫法的話,updateText是在兩個performTraversals 執(zhí)行完成之后(這時候 APP 的第一幀才顯示出來)才去執(zhí)行的, 執(zhí)行完成之后又調用了一次 performTraversals 將 TextView 的內容進行更新.

圖片的寬高是否正確顯示?

從上圖可以看到,圖片的寬高是正確顯示了出來. 原因上面已經說了,measure/layout執(zhí)行完成后,寬高的數據就可以獲取了.

是否有 Delay Load 的效果?

不一定,取決于 Delay的時長.

從前面的 Trace 圖上我們可以看到 , updateText 方法由于 Delay 了300ms, 所以在應用第一幀顯示出來170ms之后, 圖片的文字信息才進行了更新. 這個是有 Delay Load 的效果的.

但是這里只是一個簡單的TextView的更新, 如果是較大模塊的加載 , 用戶視覺上會有很明顯的 “ 空白->內容填充” 這個過程, 或者會附加”閃一下”特效…這顯然是我們不想看到的.

有人會說:可以把Delay的時間減小一點嘛,這樣就不會閃了. 話是這么說,但是由于 Android 機器的多元性(其實就是有很多高端機器,也有很多低端機器) , 在這個機子上300ms的延遲算是快,在另外一個機子上300ms算是很慢.

我們將Delay時間調整為50ms, 其Trace圖如下:

可以看到,updateText 方法在第一個 performTraversals 之后就執(zhí)行了,所以也沒有 Delay Load 的效果(雖然寬高是正確顯示了,因為在第一個 performTraversals 方法中就執(zhí)行了layout和measure).

2.3 第三種寫法

經過前兩個方法 , 我們就會想, 如果能不使用Delay方法, updateText 方法能在 第二個performTraversals 方法執(zhí)行完成后(即APP第一幀在屏幕上顯示),馬上就去執(zhí)行,那么即起到了 Delay Load的作用,又可以正確顯示圖片的寬高.

第三種寫法就是這個效果:

updateText執(zhí)行的時機?

可以看到這種寫法. updateText 在第二個 performTraversals 方法執(zhí)行完成后馬上就執(zhí)行了, 然后下一個 VSYNC 信號來了之后, TextView就更新了.

圖片的寬高是否正確顯示?

當然是正確顯示的.如圖:

是否有 Delay Load 的效果?

從 Trace 圖上看, 是有 Delay Load的效果的, 而且可以在應用第一幀顯示后馬上進行數據 Load , 不用考慮 Delay時間的長短.

3. 一些思考

關于優(yōu)化的 Delay Load 的實現,從代碼層面來看其實是非常簡單的.其帶來的效果也是很贊的.

但是實現之后我們還需要思考一下,為何這么做就可以實現這種功能呢?很顯然要回答這個問題,我們需要知道更底層的一些東西.這個還涉及到 Handler/Message/MessageQueue/Looper/VSYNC/ViewRootImpl等知識. 往大里說應該還涉及到AMS/WMS等.由于涉及到的東西比較多,我就不在這一篇里面闡述了, 下一篇文章將會從從原理上講解一下為何優(yōu)化的 Delay Load 會起作用.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末牲阁,一起剝皮案震驚了整個濱河市泻红,隨后出現的幾起案子患蹂,更是在濱河造成了極大的恐慌或颊,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件传于,死亡現場離奇詭異囱挑,居然都是意外死亡,警方通過查閱死者的電腦和手機沼溜,發(fā)現死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門平挑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人系草,你說我怎么就攤上這事通熄。” “怎么了找都?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵唇辨,是天一觀的道長。 經常有香客問我檐嚣,道長助泽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任嚎京,我火速辦了婚禮嗡贺,結果婚禮上,老公的妹妹穿的比我還像新娘鞍帝。我一直安慰自己诫睬,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布帕涌。 她就那樣靜靜地躺著摄凡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚓曼。 梳的紋絲不亂的頭發(fā)上亲澡,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音纫版,去河邊找鬼床绪。 笑死,一個胖子當著我的面吹牛其弊,可吹牛的內容都是我干的癞己。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼梭伐,長吁一口氣:“原來是場噩夢啊……” “哼痹雅!你這毒婦竟也來了?” 一聲冷哼從身側響起糊识,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤绩社,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后技掏,有當地人在樹林里發(fā)現了一具尸體铃将,經...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年哑梳,在試婚紗的時候發(fā)現自己被綠了劲阎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸠真,死狀恐怖悯仙,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情吠卷,我是刑警寧澤锡垄,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站祭隔,受9級特大地震影響货岭,放射性物質發(fā)生泄漏路操。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一千贯、第九天 我趴在偏房一處隱蔽的房頂上張望屯仗。 院中可真熱鬧,春花似錦搔谴、人聲如沸魁袜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽峰弹。三九已至,卻和暖如春芜果,著一層夾襖步出監(jiān)牢的瞬間鞠呈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工右钾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留粟按,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓霹粥,卻偏偏與公主長得像灭将,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子后控,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內容