0. 概念
冷啟動(dòng):?jiǎn)?dòng)應(yīng)用時(shí)瘪匿,后臺(tái)沒有該應(yīng)用的進(jìn)程(首次安裝萨赁、手工強(qiáng)制殺死APP進(jìn)程柑蛇、系統(tǒng)強(qiáng)制殺死APP進(jìn)程等操作)罐旗。
熱啟動(dòng):?jiǎn)?dòng)應(yīng)用時(shí),后臺(tái)已有該應(yīng)用的進(jìn)程(home鍵唯蝶、back鍵退出等操作)九秀。
1. 優(yōu)化工具
既然是優(yōu)化,肯定要能看到效果粘我,所以要有數(shù)據(jù)顯示證明優(yōu)化確實(shí)起到了作用鼓蜒。
- Displayed
在Android Studio中的logcat過濾Displayed關(guān)鍵字就可以(不包括不被布局文件及初始化對(duì)象所引用的資源的加載時(shí)間)
image.png - ADB Shell Activity Manager
看到數(shù)據(jù)和上面的差不多痹换,具體詳細(xì)操作可以網(wǎng)上搜索
adb shell am start -S -W com.android.diaodiao/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN
- reportFullyDrawn()
這次主要是采用的這個(gè)方法的數(shù)據(jù)作為標(biāo)準(zhǔn),因?yàn)轱@示的是應(yīng)用啟動(dòng)到所有資源和視圖層次結(jié)構(gòu)的完整顯示之間所經(jīng)過的時(shí)間都弹,因?yàn)橄旅嬗袘屑虞d優(yōu)化方法娇豫,所以這個(gè)方法非常有用。
主要用法就是在自己代碼中畅厢,特別是懶加載結(jié)束的時(shí)候調(diào)用
reportFullyDrawn()
告訴系統(tǒng)結(jié)束加載了冯痢,系統(tǒng)會(huì)打印出APP啟動(dòng)到這個(gè)函數(shù)調(diào)用的時(shí)間。
2. 冷啟動(dòng)分析
啟動(dòng)過程:
- 點(diǎn)擊app的啟動(dòng)圖標(biāo)時(shí)框杜,安卓系統(tǒng)會(huì)從Zygote進(jìn)程中fork創(chuàng)建出一個(gè)新的進(jìn)程分配給該應(yīng)用浦楣。
- 創(chuàng)建對(duì)象,啟動(dòng)主線程 (MainThread)咪辱。
- 初始化Application和MainActivity振劳、加載視圖(Inflating views)、渲染布局(Laying out)油狂、完成繪制(onDraw)历恐。
前兩個(gè)主要過程是:
- 點(diǎn)擊APP桌面圖標(biāo),通過Binder IPC調(diào)用ActivityManagerService
- ActivityManagerService會(huì)孵化出一個(gè)Zygote進(jìn)程.
- Zygote進(jìn)程會(huì)實(shí)例化ActivityThread對(duì)象
- ActivityThread對(duì)象會(huì)調(diào)用Looper.prepareLoop()和Looper.loop()來開啟消息循環(huán)
- ActivityThread對(duì)象會(huì)調(diào)用bindApplication方法专筷,這個(gè)方法里面會(huì)發(fā)送BIND_APPLICATION到消息隊(duì)列
- handleBindApplication會(huì)處理剛才發(fā)送的消息弱贼,然后調(diào)用makeApplication來加載APP的classes到內(nèi)存
- 在剛才啟動(dòng)的Application進(jìn)程會(huì)發(fā)送LAUNCH_ACTIVITY消息到消息隊(duì)列
- 通過handleLaunchActivity來處理消息啟動(dòng)Activity.
從以上部分可以得知主要是第三部分的優(yōu)化。
首先Android Studio提供強(qiáng)大工具來分析trace文件磷蛹,查看各個(gè)函數(shù)執(zhí)行的時(shí)間哮洽,所以要先生成trace文件。
- 在onCreate() 和 onWindowFocusChanged()中分別添加android.os.Debug.startMethodTracing()和android.os.Debug.stopMethodTracing()就會(huì)在sdcard 根目錄下或者應(yīng)用的目錄中生存trace文件弦聂,這兩個(gè)函數(shù)主要作用是生存函數(shù)執(zhí)行時(shí)間鸟辅。
- 通過 Trace.beginSection 和 Trace.endSection來追蹤應(yīng)用的代碼片段,這兩個(gè)函數(shù)主要跟蹤系統(tǒng)的 I/O 操作莺葫、內(nèi)核工作隊(duì)列匪凉、 CPU 負(fù)載等,很好收集分析 UI 顯示性能的數(shù)據(jù)捺檬。
以上兩個(gè)方法都能很明顯的找到性能瓶頸和主要耗時(shí)的操作再层。
3. 項(xiàng)目中遇到的問題
-
啟動(dòng)白屏:這個(gè)是很久以前的方案了,設(shè)置主題為自己的圖片堡纬,然后在MainActivity的onCreate里面重新設(shè)置回項(xiàng)目的主題(只是體驗(yàn)上的優(yōu)化聂受,并沒有加快app的啟動(dòng)速度)
-
異步加載:把一些能放入WorkThread里面加載的放入子線程,并且降低線程優(yōu)先級(jí)烤镐。比如友盟和數(shù)據(jù)庫(kù)初始化等
-
延時(shí)加載:必須放入主線程初始化的蛋济,如果在MainActivity加載完成立即必須用的就用View.post(Runnable) 比如IM模塊,在后續(xù)操作用的就用Handler的postDelayed盡量延時(shí)加載比如統(tǒng)計(jì)模塊炮叶。
-
懶加載:有些對(duì)象可以到用的時(shí)候在初始化就用懶加載比如ImageLoader碗旅。
-
布局優(yōu)化:MainActivity中布局的嵌套層數(shù)盡量少渡处,后續(xù)會(huì)講到,并且盡量不要有大量數(shù)據(jù)庫(kù)祟辟、IO操作医瘫。
-
其他方面:去除無用代碼、開發(fā)階段代碼旧困、重復(fù)邏輯醇份、緩存技術(shù)等。
4.總結(jié)
- 每一步優(yōu)化操作都要對(duì)癥下藥吼具,不可胡亂下藥僚纷,不然會(huì)引起新的bug。
- 優(yōu)化后的效果要用前面說的工具詳細(xì)對(duì)比馍悟,確認(rèn)優(yōu)化起到了作用畔濒。
- 有些歷史原因現(xiàn)在已經(jīng)不方便優(yōu)化剩晴,改動(dòng)太大锣咒,所以以后開發(fā)的時(shí)候要注意,盡量隔一段時(shí)間用traceView對(duì)比分析一下赞弥。