今天跟大家聊一聊,一個android app啟動過程,有哪些方法可以加快啟動速度。
先來說一說有哪些因素可能會延緩啟動速度:
1.UI線程IO操作(數據庫、網絡、SharePreference、文件讀寫、磁盤操作、文件夾創建等)
UI線程是Android中負責渲染視圖的線程,也是Android四大組建生命周期回調的主要執行線程,還是用戶交互事件的主要響應線程,所以UI線程任務繁重。
IO操作容易阻塞,且容易受出現不穩定狀況,存在各種潛在威脅。
2.靜態類中的靜態變量初始化
一個類中的任意方法,哪怕是getClass()被調用,都會導致這個類中的靜態變量被初始化,如果這些靜態變量的初始化有實例賦值且實例構造中會誘發各種耗時操作,也會對當前線程造成很大影響。
代碼事例:
public class ServiceFacade {
public static A a = new A();
public static B b = new B();
public static A getA(){
return a;
}
...
}
3.反射
反射也是一個典型的耗時操作,包括類的反射實例化、field和方法的反射遍歷、注解的反射等等,其中,field和方法的反射遍歷、注解的反射最為耗時,我們所有的很多框架中都有使用類似的反射,尤其是注解反射,如Retrofit、OrmLite、Butternife等等。當然,他們的有些版本已經將運行時反射注解改成了編譯器aapt生成靜態代碼了。
以Retrofit為例,雖然網絡請求在線程中異步執行,但是動態代理的生成仍然是在當前線程中通過反射實現的,所以使用此類框架時要注意其對性能的影響。
4.不必要的業務邏輯
這點不必多說,要讓APK快速啟動,怎容得這種勞民傷財的事情耽擱啟動大業,砍!
5.大量反復的重復查詢
萬萬不得已,有些UI線程中還有查詢數據庫的操作,或者一時改不成異步,但是也需要優化查詢方式。
6.其它線程和進程的資源搶占
BaseFragmentActivity的OnCreate()中bind了新的service進程,導致大量的資源搶占,對啟動性能造成了比較大的影響。
分析完了可能造成延緩的因素,再來說一說優化的方案:
啟動速度優化是一個系統工程,涉及的面很廣,從業務邏輯到UI渲染,從線程調度到算法優化,從性能分析到性能監控。不僅要求開發者在開發的時候有敏感的性能意識,還要求具有統顧全局的高度。用一句話概括,啟動速度優化更像是一門統籌學的實踐。
1.明確啟動的優化目標:讓用戶以最快的速度看到首屏,且不影響正常的業務邏輯。此外,以什么事件作為啟動完成的標志,也是需要明確的。
分析手段:先在啟動的關鍵路徑上打點,實踐表明,Log是最準確的有效的打點方式(對性能影響最小,對線程時序影響最小),MathodTrace對線程執行性能影響太大,且不容易在啟動過程中使用。
關鍵點:四大組件的生命周期,Application的onCreate(),Activiy的onCreate()、onStart()、onResume()、onWindowFocusChanged()
2.提前后臺加載
對于啟動中需要的數據準備、初始化等操作,可以利用多線程的形式,在后臺線程多提前加載,尤其是和IO相關的操作。
如:數據庫查詢、網絡數據請求、配置信息讀取、資源加載等;
等到主線程運行到對應節點時,便可以直接“飯來張口、衣來伸手”了。
3.延遲加載+懶加載 (“懶加載”的原則是不能“懶”!)
a. 巧用單例
單例模式是懶加載天生的伴侶,哪里用到哪里最先加載初始化,類似的思路可以應用在很多模塊的初始化上。但是也有需要注意的地方!
采用懶漢模式的單例,盡量不要在類中寫公有靜態方法。
b. 構造方法中盡量少處理邏輯
單例模式的構造方法中,盡量至保留必要的基礎邏輯,不要為了嫌麻煩把很多不必要的初始化操作放在構造方法中,因為后面不一定要用到。
c. 能懶加載的就懶加載,不要吝嗇判斷邏輯
遵循一個原則“用到再加載”。這一點,就要求在對應的邏輯上做很多類似單例的判斷,所以說,懶加載不能懶。
4.前后臺多線程多任務(統籌學)
a. ?弄清依賴關系(集中方法)
逆推法:先總結確認啟動目標的“最小集”,反向逆推構建啟動流程
刪減法:從頭至尾,以質疑的眼光剔除不必要的邏輯(先錯殺,殺不掉再救回)
b. ?統籌分配任務
規劃幾條關鍵路徑,弄清關鍵路徑之間的關鍵交點和節點
合理利用CPU資源,不宜開辟太多線程(同步異步邏輯不好處理,線程過多也降低效率)
根據打點摸清楚各個線程任務的開始結束點,合理統籌
c. ?UI線程為關鍵路徑,此路徑上的絆腳石全部踢開
UI線程上,有些必須走的生命周期回調必須盡量提前,幾乎是越往前越好,因為只有這些回調都走完了,我們目標的啟動的回調才會調出來。
d. ?后臺多線程:
IO阻塞任務(如數據庫查詢、網絡請求等),這種以IO為主要資源占用的任務,適合放在后臺線程處理,并且可以一開始就拋出去,以達到時間的最大化利用;
含有大量CPU計算工作的任務,如果不是啟動必須的,適合拋到首屏加載完成后執行;
如果是啟動必須的,也可以通過后臺線程來計算處理,理論上,多核CPU的性能可以在對應線程數下發揮到最優,當然,這個需要實驗數據證明;
5.抓住關鍵節點:onWindowFocusChanged()
onWindowFocusChanged可以粗略地作為Activity顯示的標志節點,此外還有一些節點也可以作為參考,如:IdleHandler等。
但是實驗表明IdleHandler由于某些原因不太可靠,故以保險起見棄用;
onWindowFocusChanged并不是只執行一次,故需要加入“直走一次”的邏輯。
6.優化視圖渲染
a. ?減少層級
b. ?布局文件優化(LinearLayout\RelaitveLayout\FrameLayout)
c. ?巧用ViewStub
d. ?減少大資源加載,優化大資源視圖實現
e. ?異步回調加載視圖
7.優化數據庫查詢操作
a. ?能合并成一次的操作就合并成一次
b. ?至查詢條目數量的,就至調用queryCount, 只關心某個字段的,就只查詢某個字段(大多數情況還沒必要優化到這么細致)
8.巧用內存緩存
a. ?重復獲取的數據可以使用緩存(數據庫、網絡等)
b. ?代價就是需要有額外的緩存數據同步機制,保證從緩存中獲取和從數據庫中獲取的結果一樣;
好了,以上就是這次整理的全部內容,歡迎大家補充,糾正,謝謝。