Android應用啓動優化:一種DelayLoad的實現和原理

原文章:

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

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

  1. 異步加載
  2. 延時加載
  3. 懶加載

不用一一去解釋,做過啓動優化的估計都使用過,本篇文章將詳細講解一下一種延時加載的實現以及其原理.
其實這種加載的實現是非常簡單的,但是其中的原理可能比較複雜,還涉及到Looper/Handler/MessageQueue/VSYNC等.以及其中碰到的一些問題,還會有一些我自己額外的思考.

1. 優化後的DelayLoad的實現

一提到DelayLoad,大家可能第一時間想到的就是在 onCreate 裏面調用 Handler.postDelayed方法, 將需要 Delay 加載的東西放到這裏面去初始化, 這個也是一個比較方便的方法. Delay一段時間再去執行,這時候應用已經加載完成,界面已經顯示出來了, 不過這個方法有一個致命的問題: 延遲多久?
大家都知道,在 Android 的高端機型上,應用的啓動是非常快的 , 這時候只需要 Delay 很短的時間就可以了, 但是在低端機型上,應用的啓動就沒有那麼快了,而且現在應用爲了兼容舊的機型,往往需要 Delay 較長的時間,這樣帶來體驗上的差異是很明顯的.

這裏先說優化方案:

  1. 首先 , 創建 Handler 和 Runnable 對象, 其中 Runnable 對象的 run方法裏面去更新 UI 線程.

    1
    2
    3
    4
    5
    6
    7
    
    private Handler myHandler = new Handler();
    private Runnable mLoadingRunnable = new Runnable() {
    @Override
    public void run() {
    updateText(); //更新UI線程
    }
    };
    
  2. 在主 Activity 的 onCreate 中加入下面的代碼

    1
    2
    3
    4
    5
    6
    
    getWindow().getDecorView().post(new Runnable() {
    @Override
    public void run() {
    myHandler.post(mLoadingRunnable);
    }
    });
    

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

2.1 第一種寫法

  1. updateText執行的時機?
    下面是第一種寫法的Trace圖:

    可以看到 updateText 是在 Activity 的 onCreate/onStart/onResume三個回調執行完成後纔去執行的.

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

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

  3. 是否有 Delay Load 的效果?
    並沒有. 因爲我們知道, 應用啓動的時候,要等兩次 performTraversals 都執行完成之後纔會顯示第一幀, 而 updateText 這個方法在第一個 performTraversals 執行之前就執行了. 所以 updateText 方法的執行時間是算在應用啓動的時間裏面的.

2.2 第二種寫法

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

  1. updateText執行的時機?

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

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

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

  3. 是否有 Delay Load 的效果?
    不一定,取決於 Delay的時長.
    從前面的 Trace 圖上我們可以看到 , updateText 方法由於 Delay 了300ms, 所以在應用第一幀顯示出來170ms之後, 圖片的文字信息才進行了更新. 這個是有 Delay Load 的效果的.
    但是這裏只是一個簡單的TextView的更新, 如果是較大模塊的加載 , 用戶視覺上會有很明顯的 “ 空白->內容填充” 這個過程, 或者會附加”閃一下”特效…這顯然是我們不想看到的.

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

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

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

2.3 第三種寫法

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

  1. updateText執行的時機?

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

  2. 圖片的寬高是否正確顯示?
    當然是正確顯示的.如圖:

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章