一. Android 啟動時間優化

系統啟動過程:
1)開機打開電源
2)BootLoader程序
3)Linux的 init.rc

  1. init進程
    5)Zygote的JVM
    6)SystemServer進程
    Binder線程池
    SystemServiceManager(AMS、WMS、PMS、CameraService等等被啟動)

1)啟動頁白屏及黑屏解決?
2)啟動太慢怎么解決?
3)怎么保證應用啟動不卡頓?
4)App啟動崩潰異常捕捉
5)統計啟動時長,標準

App啟動速度是用戶的第一印象,本章會介紹精準度量啟動速度的方式,啟動優化的相關工具、常規優化手段等,同時我會介紹異步初始化以及延遲初始化的最優解,以最優雅、可維護性高的的方式獲得閃電般的啟動速度。...

一. App 啟動優化介紹

App Startup time
冷啟動:耗時最多、衡量標準。
啟動App、加載空白Window、創建進程
隨后任務,創建Application、啟動主線程、創建MainActivity。

熱啟動:后臺 ——> 前臺。最快
溫啟動:較快。僅僅重走LifeCycle。

優化方向:
Application和Activity生命周期

二. 啟動時間測量方式

兩種方式:adb 命令 / 手動打點

  1. adb方式
    adb shell am start -W packagename/首屏Activity
    ThisTime:最后一個Activity啟動耗時
    TotalTime:所有Activity啟動耗時
    WaitTime:AMS啟動Activity的總耗時
    (此方式線下使用方便,不能帶到線上。非嚴謹、精確時間)

  2. 手動打點:
    啟動時埋點,啟動結束時埋點,兩者差值。
    寫個工具類,讀取SystemCurrentTimeMillis

結束時間的記錄位置?真實數據展示出來,Feed第一條展示。
onWindowFocusChanged不夠準確。
getViewTreeObserver 的 addOnPreDrawListener

三. 啟動優化工具選擇

主要兩個工具:TraceView、Systrace

1. TraceView

圖形的形式展現執行時間、調用等。信息全面,包含所有線程

使用方式:Debug.startMethodTracing("");
Debug.stopMethodTracing("");

生成文件在sd卡:Android/data/packagename/files
最大能有 8MB的信息。

圖像分析:
Top Down:Self Time、Total Time (Wall Clock Time、Thread Time)是call Chart 的詳細文字版本
Call Chart:a調用b,那么a 就在 b 的上面。系統API 橙色、應用自己的綠色,第三放的就是藍色
Flame Chart:相同調用順序的過程會合并在一起。
BottomUp:和Top Down相反。
TraceView 運行時開銷嚴重,整體都會變慢。

與CpuProfiler相比,此工具Android Studio中直接使用。可能需要手速很快才能獲取到。

2.Systrace

結合Android 內核的數據,生成一個Html報告

API 18 以上使用,推薦TraceCompact

使用方式:
python systrace.py -t 10 [other-options] [categories]

https://developer.android.com/studio/command-line/systrace#command_options

Systrace指令

查看CPU 核心運行狀況

UIThread 和 Other Threads的對比

通過Slice 查看 start 、wall Duration(代碼執行時間)、CPU Duration (代碼消耗cpu的時間)

舉例:鎖沖突

四. 優雅獲取方法耗時講解

常規方法的侵入性太強。重復編輯。

所以,采用AOP方法(Aspect Oriented Programming 面向切面編程)

  • 針對同一類問題的統一處理
  • 無侵入添加代碼

AspectJ使用:

classPath ‘com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0’

implementation 'org.aspectj:aspectjrt:1.8.+'

apply plugin: 'android-aspectjx'

Join Points:程序運行時的執行點,可以作為切面的地方。

  • 函數調用、執行
  • 獲取、設置變量
  • 類初始化

PointCut:帶條件的JoinPoints (篩選一下某些點)
一種Hook,要插入代碼的位置

Before:PointCut之前執行。Advice,具體插入位置。

After:PointCut之后執行

Around:PointCut之前和之后分別執行

execution:處理Join Point 的類型,call、execution
(* android.app.Activity.on**(..)):匹配規則

onActivityCalled:要插入的代碼

4.1優雅獲取方法耗時實操

4.2 異步優化詳解
優化技巧:
Theme切換:感覺上的快。

異步優化:
子線程分擔住線程任務,并行減少時間。

ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);

service.submit(new Runnable(){
      public void run(){
          方法分別放入;
      }
});

不符合異步要求的。部分代碼必須運行在主線程當中,那么放棄這種異步優化的方案。

需要在某階段完成。由于異步執行不知道 子任務何時結束,跳轉新界面時 該方法是否完成,

添加

private CountDownLatch mCountDownLatch = new CountDownLatch(1); // 一個簡單的開/關鎖存器

在條件被滿足后

mCountDownLatch.countDown();
try {
          mCountDownLatch.await();
} catch(InterruptedException e){
          e.printStackTrace;
}

4.3 異步初始化最優解-啟動器-1
充分利用CPU多核,自動梳理執行順序

  • 代碼Task化,啟動邏輯抽象為Task。
  • 根據所有任務以來關系排序生成一個有向無環圖
  • 多線程按照排序后的優先級依次執行

4.4 異步初始化最優解-啟動器-2
TaskDispatcher。并向當中要執行的Task 分別繼承,在run方法中進行執行。

4.5 更優秀的延遲初始化方案
常規方式,回調方法中執行耗時。很有可能導致feed 卡頓。
對延遲任務進行分批初始化。

  • 利用IdleHandler特性,空閑時執行

4.6 啟動優化其它方案
異步、延遲、懶加載。
技術、業務相結合。
cpu time才是優化方向
按照systrace 及 cpu time 跑滿cpu

IO密集型 和 CPU密集型 操作。
收斂啟動代碼修改權限。

結合Ci修改啟動代碼需要Review通知

其他方案:

  • 提前加載SharedPreferences。getSharedPreference load到內存中。如果沒有完成一直阻塞進行等待。
  • Multidex之前加載,利用此階段CPU覆寫 getApplicationContext() 返回 this。 attachBaseContext中
    啟動階段不啟動子進程,子進程會共享CPU資源,導致主進程CPU緊張。注意啟動順序:App onCreate 之前是ContentProvider

類加載優化:提前異步加載(可以替換掉系統的ClassLoader,每個類加載的時候都打印一個Log)Class.forName() 只加載類本身及其靜態變量的引用類。
new 類實例 可以額外加載類成員變量的引用類
啟動階段抑制GC

  • CPU鎖頻

啟動速度模擬面試
1)你做啟動優化是怎么做的?

  • 分析現狀、確認問題。(目前啟動流程復雜,主線程執行了太多的代碼)
  • 針對性優化(實現異步初始化)簡單異步 指向了啟動器。
  • 長期保持優化效果。

2)是怎么異步的,異步遇到問題沒有?

  • 異步演進過程
  • 詳細介紹啟動器

3)有哪些容易忽略的點?

  • cpu time 和 wall time。
  • 注意延遲初始化的優化
  • 介紹黑科技,類加載。Systrace 可能沒有給滿的CPU,拉高CPU。

4)版本迭代導致的啟動變慢有好的解決方式嗎?

  • 啟動器
  • 結合CI
  • 監控完善
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容