50. 安卓啟動優(yōu)化

啟動優(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”過濾即可


截屏2021-06-20 上午9.28.31.png

還可以通過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
截屏2021-06-20 上午9.35.08.png
截屏2021-06-20 上午9.35.29.png
截屏2021-06-20 上午9.36.35.png
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中會顯示一個這樣的界面


截屏2021-06-20 上午9.42.28.png
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)


截屏2021-06-20 上午9.45.08.png

如上圖,自定義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)用者。此時的圖表看起來越往上越窄, 就好像火焰一樣,因此得名: 火焰圖。


截屏2021-06-20 上午9.46.25.png

截屏2021-06-20 上午9.46.34.png

耗時最長的為:Thread.sleep

Top Down Tree

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

截屏2021-06-20 上午9.47.43.png

對于每個節(jié)點,三個時間信息:
Self Time —— 運行自己的代碼所消耗的時間; Children Time —— 調(diào)用其他方法的時間; Total Time —— 前面兩者時間之和。
此視圖能夠非常方便看到耗時最長的方法調(diào)用棧。

Bottom Up Tree

方便地找到某個方法的調(diào)用棧。在該列表中展開方法或函數(shù)節(jié)點會顯示哪個方法調(diào)用了自己。


截屏2021-06-20 上午9.48.41.png

通過工具可以定位到耗時代碼,然后查看是否可以進行優(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());
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 做開發(fā)除了實現(xiàn)功能,還要注重優(yōu)化,性能優(yōu)化包括的東西還是非常多的,包體大小、啟動速度、內(nèi)存、數(shù)據(jù)結(jié)構(gòu)、ANR、卡頓...
    進擊的包籽閱讀 819評論 0 5
  • 用戶希望應(yīng)用能夠及時響應(yīng)并快速加載。啟動時間過長的應(yīng)用可能會導(dǎo)致用戶在對應(yīng)用給出很低的評分,甚至完全棄用。 啟動狀...
    zcwfeng閱讀 3,207評論 1 28
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,814評論 28 54
  • 信任包括信任自己和信任他人 很多時候,很多事情,失敗、遺憾、錯過,源于不自信,不信任他人 覺得自己做不成,別人做不...
    吳氵晃閱讀 6,355評論 4 8
  • 步驟:發(fā)微博01-導(dǎo)航欄內(nèi)容 -> 發(fā)微博02-自定義TextView -> 發(fā)微博03-完善TextView和...
    dibadalu閱讀 3,380評論 1 3

友情鏈接更多精彩內(nèi)容