效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

DevYK發表於2019-04-25

效能優化系列

APP 啟動優化

UI 繪製優化

記憶體優化

圖片壓縮

長圖優化

電量優化

Dex 加解密

動態替換 Application

APP 穩定性之熱修復原理探索

APP 持續執行之程式保活實現

ProGuard 對程式碼和資源壓縮

APK 極限壓縮

簡介

效能優化的目的不是為了優化而優化,而且為了以後不再優化, 給自己統一 一個標準。

這裡也許會有人問 APP 啟動還需要優化嗎?啟動又不是我們自己寫的程式碼,難道 Google 工程師會犯這麼低階的錯嗎?其實這還真不是 Google 的錯,應該說是給我們開發者留了一個坑吧。應該有的同學知道是怎麼一回事兒了,當我們在系統桌面任意點選一個 APP 是不是會發現啟動的時候有一瞬間有白屏出現(以前老版本是黑屏) 那麼我們怎麼來優化這個黑白屏的問題勒,現在我們先來了解一下 Android 手機重開機到啟動 APP 的過程吧。

APP 啟動流程

這裡會設計到 Android 系統原始碼的知識,但並不會深入解析原始碼,我們只是瞭解一個過程,因為太深入我自己也懵。

系統的啟動

我在這裡大致分為了 6 個步驟,下面以流程圖為準

效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

啟動步驟

  1. 首先拿到一部 Android 系統的手機開啟電源,引導晶片程式碼載入載入程式 BootLoader 到 RAM 中去執行。
  2. BootLoader 把作業系統拉起來。
  3. Linux 核心啟動開始系統設定,找到一個 init.rc 檔案啟動初始化程式。
  4. init 程式初始化和啟動屬性服務,之後開啟 Zygote 程式。
  5. Zygote 開始建立 JVM 並註冊 JNI 方法,開啟 SystemServer。
  6. 啟動 Binder 執行緒池和 SystemServiceManager,並啟動各種服務。

Launcher 啟動

App Appcation 啟動

  1. 手機回到系統桌面, 通過 adb shell dumpsys window w |findstr / |findstr name= 來檢視當前的程式和 Activity 名。

    效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

  2. 當點選桌面 APP 圖示的時候會走 Launcher . java 的 onClick (View view) 方法,詳細見下圖。

    效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

    startActivity(intent) 會開啟一個 APP 程式

  3. AcitivityThread main() 呼叫執行流程,見下圖。

    效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

    最後 ActivityThread main() 是通過反射來進行初始化的

  4. ActivityThread.java 做為入口,詳細解說 main() 函式,還是以一個動畫來演示一下吧;

    效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

    根據上面的動畫,大家應該已經明白 ActivityThread.java main() 方法中 Appcation onCreate() 的是怎麼被呼叫起來的吧。

    注意:

    不知道大家有沒有注意 ActivityThread main() 中 Looper.prepareMainLooper(); 其實我們們為什麼能夠在 Main Thread 中建立 Handler 不會報錯了吧,是因為 Activity 啟動的時候在這裡已經預設開啟了 Looper。

APP 啟動黑白屏問題

終於到了正題了,下面我們就來說下啟動黑白屏的問題,還是先來看一個 GIF 吧。

市面上 APP 黑白屏

效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

​ 從上面的一段錄屏我們可以發現市面上常見的 APP 啟動有的是白屏有的是做了優化。黑屏只有在 Android 4.n 具體是哪個版本我也忘了。那麼現在我們就以我現在的真實專案來優化一下啟動。

真實專案中優化

簡介

首先為什麼會造成白屏勒我們來看一段原始碼

效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

最後就是這個 windowBackground 搞的鬼,知道了是這個搞的鬼那麼我們就可以來進行優化了。

優化方案 一

在自己的 AppTheme 中加入 windowBackground 
複製程式碼

優化方案 二

設定 windowbackgroud 為透明的

<item name="android:windowIsTranslucent">true</item>
複製程式碼

但是:

​ 這 2 中方法會有一個問題,就是所有的 Activity 啟動都會顯示。

優化方案 三

  1. 單獨做成一個 AppTheme.Launcher
    <style name="AppTheme.Launcher">
        <item name="android:windowFullscreen">true</item>
        <!--<item name="android:windowDisablePreview">true</item>-->
        <item name="android:windowBackground">@color/colorAccent</item>
    </style>
複製程式碼
  1. 在清單檔案中 啟動 Activity 加入該 主題

            <activity
                android:name="com.t01.android.dida_login.mvp.ui.activity.LoginActivity"
                android:configChanges="keyboardHidden|orientation|screenSize"
                android:theme="@style/AppTheme.Launcher"
                android:windowSoftInputMode="adjustUnspecified|stateHidden">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    複製程式碼
  2. 在啟動 Activity 頁面中加入

      setTheme(R.style.AppTheme_Launcher);
    複製程式碼

最後這樣做只有啟動的 UI 才能見到自己的樣式

  1. 最後效果,因為我這裡沒有背景圖,故弄了一個主題顏色,如果想要設定一張背景圖片可以參考下面的示例,不然有可能會引起圖片拉伸效果。

    我這裡啟動時間大概在 500 ms ~ 800 ms 左右。

    效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item>
            <bitmap android:src="@mipmap/app_bg"
                android:gravity="fill"/>
        </item>
    </layer-list>
    複製程式碼

    最後在清單 啟動 Activity 的 Theme 中修改為

    <item name="android:windowBackground">@drawable/app_theme_bg</item>
    複製程式碼
  2. 據說 QQ 的實現方法是(這裡只做參考,感興趣的同學可以自己試試。)

     <item name="android:windowDisablePreview">true</item>
     <item name="android:windowBackground">@null</item>
    複製程式碼

啟動時間檢視

4.4 以前版本檢視

adb shell am start -W packName/activity 全路徑
複製程式碼

4.4 版本以後檢視方式

通過關鍵字 Displayed 並篩選為 No Filters

2019-04-25 18:35:57.629 508-629/? I/ActivityManager: Displayed com.lingyi.autiovideo.lykj/com.t01.android.dida_login.mvp.ui.activity.LoginActivity: +844ms
複製程式碼

工具分析程式碼執行

Appcation 中檢視耗時通過(如果有的同學還用 Log 列印系統時間來相減來檢視 耗時的話,看完我這篇文章就可以換成下面方法了,不然就有點 LOW 了哈)

//開始計時
Debug.startMethodTracing(filePath);
     中間為需要統計執行時間的程式碼
//停止計時
Debug.stopMethodTracing();
複製程式碼

還是通過一組動畫來看我怎麼操作的吧。(注意這裡的時間是 微妙 微妙/10^6 = s 應該是這樣,忘了)

效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)
這個工具可以很友好的提示每個函式具體在內部執行了多少時間,卡頓其實也可以用這個方法來進行監測

匯出 trace 檔案命令

adb pull /storage/emulated/0/appcation_launcher_time.trace
複製程式碼

我這裡耗時還不算太大 大概在 0.2 - 0.3 s 左右。

Appcation 中優化方案(並不絕對,優化思路差不多)

  1. 開子執行緒
    • 執行緒中沒有建立 Handler、沒有操作 UI 、對非同步要求不高
  2. 懶載入
    • 用到的時候在初始化,如網路,資料庫,圖片庫,或一些三方庫。
  3. 使用 IntentService onHandleIntent () 方法來進行初始化一些比較耗時的操作

總結

以思維導圖總結一下目前啟動優化的方案:

效能優化(一)APP 啟動優化(不敢說秒開,但是最終優化完真不到 1s)

相關文章