啓動優化包含app的啓動和單頁面的啓動,今天只說app的啓動,二者優化的邏輯是相同的。
app啓動的三種狀態
冷啓動
此時需要創建進程,表示應用的首次啓動
熱啓動
應用的所有 Activity 仍駐留在內存中,不必重複執行對象初始化、佈局加載和繪製。
溫啓動
如:用戶在退出應用後又重新啓動應用。進程可能未被銷燬,繼續運行,但應用需要執行 onCreate() 從頭開始重新創建 Activity
Google 提出的Android Vitals計劃建議:冷啓動控制在 5 秒內, 溫啓控制在2秒內, 熱啓動控制在1.5 秒內。
app啓動分析
如何查看app啓動時間
Android 4.4(API 級別 19)及更高版本中,logcat 包含一個輸出行,代表從啓動進程到在屏幕上完成對應 Activity 的繪製所用的時間,用“ActivityManager: Displayed”過濾即可
還可以通過adb命令查看
adb shell am start -S -W com.rzm.easy.demo/.MainActivity -c android.intent.category.LAUNCHER -a android.intent.action.MAIN </pre>
啓動完成後,將輸出:
ThisTime: 415 表示一連串啓動Activity的最後一個Activity的啓動耗時
TotalTime: 415 真正啓動的耗時,表示新應用啓動的耗時,包括新進程的啓動和Activity的啓動,但不包括前一個應用Activity pause 的耗時
WaitTime: 437 總的耗時,包括前一個應用Activity pause的時間和新應用啓動的時間;
定位代碼中的耗時情況-CPU Profile
Sample Java Methods
對 Java 方法採樣:在應用的 Java 代碼執行期間,頻繁捕獲應用的調用堆棧。分析器會比較捕獲的數據集, 以推導與應用的 Java 代碼執行有關的時間和資源使用信息。如果應用在捕獲調用堆棧後進入一個方法並在下 次捕獲前退出該方法,分析器將不會記錄該方法調用
Trace Java Methods
跟蹤 Java 方法:在運行時檢測應用,以在每個方法調用開始和結束時記錄一個時間戳。系統會收集並比較這些時間戳,以生成方法跟蹤數據,包括時間信息和 CPU 使用率。
Sample C/C++ Functions,對 C/C++
函數採樣:捕獲應用的原生線程的採樣跟蹤數據。(Android 8.0(API 級別 26)或更高版本)
Trace System Calls
跟蹤系統調用:捕獲非常翔實的細節,以便檢查應用與系統資源的交互情況。可以檢查線程狀態的確切 時間和持續時間、直觀地查看所有內核的 CPU 瓶頸在何處,並添加要分析的自定義跟蹤事件。( Android 7.0(API 級別 24)或更高版本)
profile中會顯示一個這樣的界面
Call Chart
以圖形來呈現方法跟蹤數據或函數跟蹤數據,其中調用的時間段和時間在橫軸上表示(橫軸越長,消耗時間越長),而其被調用方則在縱軸上顯 示。對系統 API 的調用顯示爲橙色,對應用自有方法的調用顯示爲綠色,對第三方 API(包括 Java 語言 API)的調 用顯示爲藍色。 (實際顏色顯示有Bug)
如上圖,自定義Application的 onCreate 調用了 Thread.sleep 耗時爲:3s。
Call Chart 已經比原數據可讀性高很多,但它仍然不方便發現那些運行時間很長的代碼,這時我們便需要使用
Flame Chart。
Flame Chart
提供一個倒置的調用圖表,用來彙總完全相同的調用堆棧。也就是說,將具有相同調用方順序的完全相同的方法或 函數收集起來,並在火焰圖中將它們表示爲一個較長的橫條 。
橫軸顯示的是百分比數值。由於忽略了時間線信息,Flame Chart 可以展示每次調用消耗時間佔用整個記錄時長的 百分比。 同時縱軸也被對調了,在頂部展示的是被調用者,底部展示的是調用者。此時的圖表看起來越往上越窄, 就好像火焰一樣,因此得名: 火焰圖。
耗時最長的爲:Thread.sleep
Top Down Tree
如果我們需要更精確的時間信息,就需要使用 Top Down Tree。 Top Down Tree顯示一個調用列表,在該列表中 展開方法或函數節點會顯示它調用了的方法節點。
對於每個節點,三個時間信息:
Self Time —— 運行自己的代碼所消耗的時間; Children Time —— 調用其他方法的時間; Total Time —— 前面兩者時間之和。
此視圖能夠非常方便看到耗時最長的方法調用棧。
Bottom Up Tree
方便地找到某個方法的調用棧。在該列表中展開方法或函數節點會顯示哪個方法調用了自己。
通過工具可以定位到耗時代碼,然後查看是否可以進行優化。對於APP啓動來說,啓動耗時包括Android系統啓動 APP進程加上APP啓動界面的耗時時長,我們可做的優化是APP啓動界面的耗時,也就是說從Application的構建到 主界的 onWindowFocusChanged 的這一段時間。
Debug API
除了直接使用 Profile 啓動之外,我們還可以藉助Debug API生成trace文件。
public class MyApplication extends Application {
public MyApplication() {
Debug.startMethodTracing("ram");
}
}
public class MainActivity extends AppCompatActivity {
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Debug.stopMethodTracing(); </pre>
}
}
運行App,則會在sdcard中生成一個rzm.trace文件(需要sdcard讀寫權限)。將手機中的trace文件保存至電 腦,隨後拖入Android Studio即可。
StrictMode嚴苛模式
StrictMode是一個開發人員工具,它可以檢測出我們可能無意中做的事情,並將它們提請我們注意,以便我們能夠
修復它們。
StrictMode最常用於捕獲應用程序主線程上的意外磁盤或網絡訪問。幫助我們讓磁盤和網絡操作遠離主線程,可以 使應用程序更加平滑、響應更快。
public class MyApplication extends Application {
@Override
public void onCreate() {
if (BuildConfig.DEBUG) {
//線程檢測策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads() //讀、寫操作
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems .penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects() //Sqlite對象泄露
.detectLeakedClosableObjects() //未關閉的Closable對象泄露 .penaltyLog() //違規打印日誌
.penaltyDeath() //違規崩潰
.build());
}
}
}