IdleHandler,頁面啟動最佳化神器

longmanma發表於2021-09-09

圖片描述

隨著App的開發到了某個階段必然會遇到一個需求,那就是最佳化頁面的啟動時間。

第一個問題:有什麼方法可以去統計頁面的啟動時間呢?

adb logcat -s ActivityManager | grep "Displayed"

上面的命令列可用來進行檢視。

第二個問題:啟動時間是包括了哪些流程,是如何被計算出來的呢?

App啟動主要經過如下幾個流程

  1. Launch the process.

  2. Initialize the objects.

  3. Create and initialize the activity.

  4. Inflate the layout.

  5. Draw your application for the first time.

最末尾的步驟5是繪製你的介面。所以完整的啟動時間是要到繪製完成為止。

那麼繪製介面對應的是什麼時候呢?一般我們開發,最晚能被回撥的是在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);

看上面的程式碼,就先放結論了。

在performResumeActivity 中進行了onResume的回撥,在wm.addView 中進行了繪製,因此onResume的方法是在繪製之前,在onResume中做一些耗時操作都會影響啟動時間。

下面就剝一下onResume的邏輯,繪製的有興趣可以自己看原始碼。 首先performResumeActivity中會呼叫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中呼叫了 mInstrumentation.callActivityOnResume(this);

 final void performResume() { //省略部分程式碼
 mInstrumentation.callActivityOnResume(this); //省略部分程式碼
 }

最後在callActivityOnResume 呼叫了onResume

 public void callActivityOnResume(Activity activity) {
 activity.mResumed = true;
 activity.onResume(); //省略程式碼
 }

到了此處就算真正呼叫到了onResume的方法。

既然知道了onResume中做的操作會影響到啟動時間,那麼就有一個最佳化啟動時間的思路了。

思路

把在onResume以及其之前的呼叫的但非必須的事件(如某些介面View的繪製)挪出來找一個時機(即繪製完成以後)去呼叫。那樣啟動時間自然就縮短了。但是整體做的事並沒有明顯變化。那麼這個時機是什麼呢?

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();
 }

從這個原始碼可知道,IdleHandler即在looper裡面的message處理完了的時候去呼叫,這不就是我們onResume呼叫完了以後的時機麼。

來一張圖說明一下,明顯的IdleHandler在onResume以及performTraversals繪製之後呼叫

圖片描述

由這個思路我把自己負責的頁面中的一些介面的繪製邏輯挪到了IdleHandler中,由於有LoadingView時間,我把Adapter的繫結也挪出去了。看下最佳化前後效果圖 ,效果還是挺明顯的;

圖片描述

圖片描述



作者:Android高階架構
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2730/viewspace-2821712/,如需轉載,請註明出處,否則將追究法律責任。

相關文章