啟動優(yōu)化包含app的啟動和單頁面的啟動,今天只說app的啟動,二者優(yōu)化的邏輯是相同的。
app啟動的三種狀態(tài)
冷啟動
此時需要創(chuàng)建進程,表示應(yīng)用的首次啟動
熱啟動
應(yīng)用的所有 Activity 仍駐留在內(nèi)存中,不必重復(fù)執(zhí)行對象初始化、布局加載和繪制。
溫啟動
如:用戶在退出應(yīng)用后又重新啟動應(yīng)用。進程可能未被銷毀,繼續(xù)運行,但應(yīng)用需要執(zhí)行 onCreate() 從頭開始重新創(chuàng)建 Activity
Google 提出的Android Vitals計劃建議:冷啟動控制在 5 秒內(nèi), 溫啟控制在2秒內(nèi), 熱啟動控制在1.5 秒內(nèi)。
app啟動分析
如何查看app啟動時間
Android 4.4(API 級別 19)及更高版本中,logcat 包含一個輸出行,代表從啟動進程到在屏幕上完成對應(yīng) 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 真正啟動的耗時,表示新應(yīng)用啟動的耗時,包括新進程的啟動和Activity的啟動,但不包括前一個應(yīng)用Activity pause 的耗時
WaitTime: 437 總的耗時,包括前一個應(yīng)用Activity pause的時間和新應(yīng)用啟動的時間;
定位代碼中的耗時情況-CPU Profile



Sample Java Methods
對 Java 方法采樣:在應(yīng)用的 Java 代碼執(zhí)行期間,頻繁捕獲應(yīng)用的調(diào)用堆棧。分析器會比較捕獲的數(shù)據(jù)集, 以推導(dǎo)與應(yīng)用的 Java 代碼執(zhí)行有關(guān)的時間和資源使用信息。如果應(yīng)用在捕獲調(diào)用堆棧后進入一個方法并在下 次捕獲前退出該方法,分析器將不會記錄該方法調(diào)用
Trace Java Methods
跟蹤 Java 方法:在運行時檢測應(yīng)用,以在每個方法調(diào)用開始和結(jié)束時記錄一個時間戳。系統(tǒng)會收集并比較這些時間戳,以生成方法跟蹤數(shù)據(jù),包括時間信息和 CPU 使用率。
Sample C/C++ Functions,對 C/C++
函數(shù)采樣:捕獲應(yīng)用的原生線程的采樣跟蹤數(shù)據(jù)。(Android 8.0(API 級別 26)或更高版本)
Trace System Calls
跟蹤系統(tǒng)調(diào)用:捕獲非常翔實的細節(jié),以便檢查應(yīng)用與系統(tǒng)資源的交互情況。可以檢查線程狀態(tài)的確切 時間和持續(xù)時間、直觀地查看所有內(nèi)核的 CPU 瓶頸在何處,并添加要分析的自定義跟蹤事件。( Android 7.0(API 級別 24)或更高版本)
profile中會顯示一個這樣的界面

Call Chart
以圖形來呈現(xiàn)方法跟蹤數(shù)據(jù)或函數(shù)跟蹤數(shù)據(jù),其中調(diào)用的時間段和時間在橫軸上表示(橫軸越長,消耗時間越長),而其被調(diào)用方則在縱軸上顯 示。對系統(tǒng) API 的調(diào)用顯示為橙色,對應(yīng)用自有方法的調(diào)用顯示為綠色,對第三方 API(包括 Java 語言 API)的調(diào) 用顯示為藍色。 (實際顏色顯示有Bug)

如上圖,自定義Application的 onCreate 調(diào)用了 Thread.sleep 耗時為:3s。
Call Chart 已經(jīng)比原數(shù)據(jù)可讀性高很多,但它仍然不方便發(fā)現(xiàn)那些運行時間很長的代碼,這時我們便需要使用
Flame Chart。
Flame Chart
提供一個倒置的調(diào)用圖表,用來匯總完全相同的調(diào)用堆棧。也就是說,將具有相同調(diào)用方順序的完全相同的方法或 函數(shù)收集起來,并在火焰圖中將它們表示為一個較長的橫條 。
橫軸顯示的是百分比數(shù)值。由于忽略了時間線信息,F(xiàn)lame Chart 可以展示每次調(diào)用消耗時間占用整個記錄時長的 百分比。 同時縱軸也被對調(diào)了,在頂部展示的是被調(diào)用者,底部展示的是調(diào)用者。此時的圖表看起來越往上越窄, 就好像火焰一樣,因此得名: 火焰圖。


耗時最長的為:Thread.sleep
Top Down Tree
如果我們需要更精確的時間信息,就需要使用 Top Down Tree。 Top Down Tree顯示一個調(diào)用列表,在該列表中 展開方法或函數(shù)節(jié)點會顯示它調(diào)用了的方法節(jié)點。

對于每個節(jié)點,三個時間信息:
Self Time —— 運行自己的代碼所消耗的時間; Children Time —— 調(diào)用其他方法的時間; Total Time —— 前面兩者時間之和。
此視圖能夠非常方便看到耗時最長的方法調(diào)用棧。
Bottom Up Tree
方便地找到某個方法的調(diào)用棧。在該列表中展開方法或函數(shù)節(jié)點會顯示哪個方法調(diào)用了自己。

通過工具可以定位到耗時代碼,然后查看是否可以進行優(yōu)化。對于APP啟動來說,啟動耗時包括Android系統(tǒng)啟動 APP進程加上APP啟動界面的耗時時長,我們可做的優(yōu)化是APP啟動界面的耗時,也就是說從Application的構(gòu)建到 主界的 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讀寫權(quán)限)。將手機中的trace文件保存至電 腦,隨后拖入Android Studio即可。
StrictMode嚴苛模式
StrictMode是一個開發(fā)人員工具,它可以檢測出我們可能無意中做的事情,并將它們提請我們注意,以便我們能夠
修復(fù)它們。
StrictMode最常用于捕獲應(yīng)用程序主線程上的意外磁盤或網(wǎng)絡(luò)訪問。幫助我們讓磁盤和網(wǎng)絡(luò)操作遠離主線程,可以 使應(yīng)用程序更加平滑、響應(yīng)更快。
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() //未關(guān)閉的Closable對象泄露 .penaltyLog() //違規(guī)打印日志
.penaltyDeath() //違規(guī)崩潰
.build());
}
}
}