Android 分析實現性能優化之啟動速度優化

本文主要探討以下幾個問題:

  • 啟動方式
  • 啟動流程中可優化的環節
  • 檢測工具
  • 優化點
  • 黑白屏問題

啟動方式

應用有三種啟動狀態,每種狀態都會影響應用向用戶顯示所需的時間:冷啟動、溫啟動與熱啟動

冷啟動(啟動優化目標)

冷啟動是指應用從頭開始啟動:系統進程在冷啟動後才創建應用進程。發生冷啟動的情況包括應用自設備啟動後或系統終止應用後首次啟動。

熱啟動

在熱啟動中,系統的所有工作就是將 Activity 帶到前臺。隻要應用的所有 Activity 仍駐留在內存中,應用就不必重復執行對象初始化、佈局加載和繪制。

溫啟動

溫啟動包含瞭在冷啟動期間發生的部分操作;同時,它的開銷要比熱啟動高。有許多潛在狀態可視為溫啟動。例如:

  • 用戶在退出應用後又重新啟動應用。進程可能未被銷毀,繼續運行,但應用需要執行 onCreate() 從頭開始重新創建 Activity。
  • 系統將應用從內存中釋放,然後用戶又重新啟動它。進程和 Activity 需要重啟,但傳遞到 onCreate() 的已保存的實例savedInstanceState對於完成此任務有一定助益。

啟動流程中可優化的環節

啟動流程中開發者可優化的環節不多,咱們可以從APP啟動流程中尋找下

在這裡插入圖片描述

APP啟動過程如下:

  • 點擊桌面App圖標,Launcher進程采用Binder IPC向system_server進程發起startActivity請求;
  • system_server進程接收到請求後,向zygote進程發送創建進程的請求;
  • Zygote進程fork出新的子進程,即App進程;
  • App進程,通過Binder IPC向sytem_server進程發起attachApplication請求;
  • system_server進程在收到請求後,進行一系列準備工作後,再通過binder IPC向App進程發送scheduleLaunchActivity請求;
  • App進程的binder線程(ApplicationThread)在收到請求後,通過handler向主線程發送LAUNCH_ACTIVITY消息;
  • 主線程在收到Message後,通過反射機制創建目標Activity,並回調Activity.onCreate()等方法。
  • 到此,App便正式啟動,開始進入Activity生命周期,執行完onCreate/onStart/onResume方法,UI渲染結束後便可以看到App的主界面。

所以,我們能優化的階段隻有 Application.onCreate() —> Activity.onWindowFocusChanged()

檢測工具

啟動時間檢測

啟動的時間怎樣算是合適的?怎樣一個時間范圍內用戶是感覺流暢的?Android Vitals在您的應用出現以下情況時將其啟動時間視為過長:

  • 冷啟動用瞭 5 秒或更長時間
  • 溫啟動用瞭 2 秒或更長時間
  • 熱啟動用瞭 1.5 秒或更長時間

那APP啟動用瞭多長時間?用什麼區檢測?

Logcat Displayed

在 Android 4.4(API 級別 19)及更高版本中,logcat 包含一個輸出行,其中包含名為 Displayed 的值。此值代表從啟動進程到在屏幕上完成對應 Activity 的繪制所用的時間。

在這裡插入圖片描述

adb 命令統計

adb shell am start -S -W [packageName]/[activityName]

C:\Users\****>adb shell
generic_x86_arm:/ $ am start -S -W com.miss.misslink/.MainActivity
Stopping: com.miss.misslink
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.miss.misslink/.MainActivity }
Status: ok
LaunchState: COLD
Activity: com.miss.misslink/.MainActivity
TotalTime: 941
WaitTime: 961
Complete

WaitTime:包括前一個應用Activity pause的時間和新應用啟動的時間;
ThisTime:表示一連串啟動Activity的最後一個Activity的啟動耗時;
TotalTime:表示新應用啟動的耗時,包括新進程的啟動和Activity的啟動,但不包括前一個應用Activity pause的耗時。

在這裡插入圖片描述

我們一般隻用 TotalTime,可以很清楚的知道APP的啟動時間。那我們如何判斷是哪些方法耗時太多導致APP啟動時間過長呢?

CPU profile

API level >= 26

要在應用啟動過程記錄CPU活動,需要做以下操作

1.依次選擇 Run > Edit Configurations

在這裡插入圖片描述

2.勾選 Trace Java Methods(跟蹤 Java 方法:在運行時檢測應用,以在每個方法調用開始和結束時記錄一個時間戳。系統會收集並比較這些時間戳,以生成方法跟蹤數據,包括時間信息和 CPU 使用率。)

在這裡插入圖片描述

3. 依次選擇 Run > Profile,將您的應用部署到搭載 Android 8.0(API 級別 26)或更高版本的設備上

主要分析的地方有3個: Flame Chart、 Top Down 、bottom up。

在這裡插入圖片描述

API level < 26

對於API低於26的,我們可以調用 Debug API,調用起點 Application構造函數

public class MyApplication extends Application {
    
    public MyApplication() {
        //  沒有指定絕對路徑,就是相對路徑,相對 sdcard
        Debug.startMethodTracing("miss");
    }
    
}

調用終點 Activity.onWindowFocusChanged()

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        Debug.stopMethodTracing();
    }

如此一來會在 sdcard 路徑下生成 miss 文件,雙擊打開即可

StrictMode 嚴苛模式

StrictMode是一個開發人員工具,它可以檢測出我們可能無意中做的事情,並將它們提請我們註意,以便我們能夠修復它們。StrictMode最常用於捕獲應用程序主線程上的意外磁盤或網絡訪問。幫助我們讓磁盤和網絡操作遠離主線程,可以使應用程序更加平滑、響應更快

	//	Application onCreate 中使用
    @Override
    public void onCreate() {
        if (BuildConfig.DEBUG) {
            //線程檢測策略
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()   //讀、寫操作
                    .detectDiskWrites()
                    .detectNetwork()   // or .detectAll() for all detectable problems
                    .penaltyLog()
                    .penaltyDeath()
                    .build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectLeakedSqlLiteObjects()   //Sqlite對象泄露
                    .detectLeakedClosableObjects()  //未關閉的Closable對象泄露
                    .penaltyLog()  //違規打印日志
                    .penaltyDeath() //違規崩潰
                    .build());
        }

優化點

  • 合理的使用異步初始化、延遲初始化、懶加載機制。
  • 啟動過程避免耗時操作,如數據庫 I/O操作不要放在主線程執行。
  • 類加載優化:提前異步執行類加載。
  • 合理使用IdleHandler進行延遲初始化。
  • 簡化佈局
  • 第三方庫有些是有優化插件的,比如ARouter

黑白屏問題

當系統加載並啟動 App 時,需要耗費相應的時間,這樣會造成用戶會感覺到當點擊 App 圖標時會有 “延遲” 現象,為瞭解決這一問題,Google 的做法是在 App 創建的過程中,先展示一個空白頁面,讓用戶體會到點擊圖標之後立馬就有響應。
如果你的application或activity啟動的過程太慢,導致系統的BackgroundWindow沒有及時被替換,就會出現啟動時白屏或黑屏的情況(取決於Theme主題是Dark還是Light)。消除啟動時的黑/白屏問題,大部分App都采用自己在Theme中設置背景圖的方式來解決。

<style name="AppTheme.Launcher">
	<item name="android:windowBackground">@drawable/bg</item>
</style>
<activity
	android:name=".activity.SplashActivity"
	android:screenOrientation="portrait"
	android:theme="@style/AppTheme.Launcher">
	<intent-filter>
		<action android:name="android.intent.action.MAIN" />
		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
</activity>

然後在Activity的onCreate方法,把Activity設置回原來的主題

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);
    }

這麼做,隻是提高啟動的用戶體驗。並不能做到真正的加快啟動速度。

總結:啟動速度優化也會涉及到佈局優化與卡頓優化,包括內存抖動等問題。優化是一條持續的道路,很多時候我們會發現通過各種檢測手段花費瞭大量的精力去對代碼進行修改得到的優化效果可能並不理想。因為優化就是一點一滴積累下來的,我們平時在編碼的過程中就需要多註意自己的代碼性能。

到此這篇關於Android 分析實現性能優化之啟動速度優化的文章就介紹到這瞭,更多相關Android 性能優化內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: