一個(gè)留得住用戶的APP體驗(yàn)感一定要好惊畏,應(yīng)用的啟動(dòng)越快恶耽,用戶體驗(yàn)自然就好,用戶體驗(yàn)差颜启,等了半天都沒(méi)出來(lái)偷俭,第一影響就不好,所以我們需要對(duì)應(yīng)用啟動(dòng)以及黑白屏進(jìn)行處理缰盏,快速啟動(dòng)涌萤,給用戶一個(gè)好的體驗(yàn)。最近因?yàn)轫?xiàng)目需要開(kāi)始看這個(gè)問(wèn)題乳规,學(xué)習(xí)一下如何能盡快的啟動(dòng)應(yīng)用形葬。所以就找來(lái)了官方的文檔看看合呐,自己翻譯和選擇性總結(jié)了一下暮的。所以本篇文章和官網(wǎng)不是完全相同。如果有哪里不對(duì)的淌实,歡迎留言改正冻辩。雖然我很多次都希望自己是一個(gè)可以把英文一眼就看穿的人,但是能力還有限拆祈,所以可能哪里不對(duì)恨闪。大家多提出來(lái)。謝謝7呕怠咙咽!好吧廢話這么多了。開(kāi)始吧淤年!
首先钧敞,應(yīng)用啟動(dòng)分為三個(gè)狀態(tài):1.冷啟動(dòng) 2.熱啟動(dòng) 3.溫啟動(dòng)蜡豹。其中冷啟動(dòng)是包括了啟動(dòng)APP,創(chuàng)建APP進(jìn)程,顯示startWindow界面的溉苛。而熱啟動(dòng)和溫啟動(dòng)是界面從不可見(jiàn)到可見(jiàn)的過(guò)程镜廉。我們著重于優(yōu)化冷啟動(dòng),當(dāng)然熱啟動(dòng)和溫啟動(dòng)也可以優(yōu)化愚战。
對(duì)了娇唯,本片文章是Android應(yīng)用快速啟動(dòng)的系列文章的第一篇,也是基礎(chǔ)知識(shí)寂玲。接下來(lái)我會(huì)對(duì)本文中提到的一些知識(shí)塔插,以及工具的使用總結(jié)跟大家分享。敬請(qǐng)期待吧. 寫(xiě)系列文章有利于對(duì)一個(gè)知識(shí)點(diǎn)的持續(xù)跟蹤拓哟,對(duì)我是一種自律和督促佑淀。你也可以試試!看看我能不能堅(jiān)持下去彰檬,如果我可以你也可以伸刃!雞湯一波,加油逢倍!
冷啟動(dòng)
只有應(yīng)用啟動(dòng)了捧颅,系統(tǒng)才會(huì)創(chuàng)建應(yīng)用進(jìn)程。冷啟動(dòng)發(fā)生在應(yīng)用第一次啟動(dòng)或者被系統(tǒng)殺死之后啟動(dòng)的時(shí)候较雕。
冷啟動(dòng)的過(guò)程:
- 系統(tǒng)層面
- 加載app
- app加載成功之后碉哑,顯示一個(gè)空白的啟動(dòng)窗口(就是我們平時(shí)啟動(dòng)應(yīng)用看到的黑白屏)。
- 創(chuàng)建應(yīng)用進(jìn)程
系統(tǒng)加載完之后就輪到應(yīng)用了亮蒋。
- 應(yīng)用層面
- 創(chuàng)建APP對(duì)象
- 加載主線程
- 創(chuàng)建主Activity
- 加載View
- 布局
- 繪制視圖
一旦App線程完成了繪制扣典,系統(tǒng)就會(huì)用主Activity替換到空白的啟動(dòng)窗口。至此慎玖,用戶可以開(kāi)始交互了贮尖。
從官網(wǎng)上搞了一張圖來(lái)說(shuō)明一下啟動(dòng)過(guò)程。look
在創(chuàng)建APP和創(chuàng)建Activity時(shí)容易引發(fā)性能問(wèn)題趁怔。
APP Creation
主要工作是創(chuàng)建APP對(duì)象湿硝,主線程以及主Activity
Activity Creation
1.初始化數(shù)據(jù)
- 調(diào)用構(gòu)造函數(shù)
- 調(diào)用oncreate
onCreate函數(shù)的工作多少和啟動(dòng)時(shí)間息息相關(guān)。 因?yàn)樗虞d布局润努,初始化activity運(yùn)行的對(duì)象关斜。
啟動(dòng)性能分析
-
初始顯示的時(shí)間
logcat獲取時(shí)間
從Android4.4(API 19)開(kāi)始,logcat會(huì)打印一條關(guān)于acitivty顯示時(shí)間的信息--
Displayed
.這個(gè)時(shí)間包括了啟動(dòng)Activity到Layout顯示的過(guò)程铺浇。包含了一下事件:- 啟動(dòng)APP進(jìn)程
- 初始化APP對(duì)象
- 創(chuàng)建初始化activity
- 加載view
- 首次繪制應(yīng)用
打印的樣子呢就長(zhǎng)的跟這個(gè)差不多啦痢畜!
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
注意這個(gè)是系統(tǒng)打印的哦,所以不要再logcat里面通過(guò)應(yīng)用包名來(lái)過(guò)濾,否則你看不到的呢丁稀。
** PS:
Logcat輸出中的“顯示”度量不一定會(huì)捕獲所有資源加載和顯示之前的時(shí)間量:它會(huì)遺漏布局文件中未引用的資源或應(yīng)用程序作為對(duì)象初始化的一部分創(chuàng)建的資源繁涂。 它排除了這些資源,因?yàn)榧虞d它們是一個(gè)內(nèi)嵌的過(guò)程二驰,并且不會(huì)阻止應(yīng)用程序的初始顯示扔罪。所以懶加載數(shù)據(jù)的時(shí)間是不會(huì)計(jì)算到這里面的。切記M叭浮?蠼汀!矗积!**
ADB 命令獲取時(shí)間
adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN
abd管理界面或者串口輸入如上命令全肮,就可以獲取到啟動(dòng)某個(gè)Activity花費(fèi)的時(shí)間。
最簡(jiǎn)單的語(yǔ)句就是:
adb shell am start -S -W xxxActivity的完整路徑
-c棘捣,-a使用來(lái)指定category和action的可選項(xiàng)辜腺。
結(jié)果類(lèi)似:
Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete
ThisTime:最后一個(gè)啟動(dòng)的Activity的啟動(dòng)耗時(shí)
TotalTime:自己的所有Activity的啟動(dòng)耗時(shí)
WaitTime: ActivityManagerService啟動(dòng)App的Activity時(shí)的總時(shí)間(包括當(dāng)前Activity的onPause()和自己Activity的啟動(dòng))
- 總的顯示時(shí)間 (Fulltime)
通過(guò)調(diào)用reportFullyDrawn()方法可以獲取到從應(yīng)用啟動(dòng)到資源加載完成的全部時(shí)間。即包括了懶加載的時(shí)間乍恐,不過(guò)依然記住懶加載是不會(huì)影響應(yīng)用啟動(dòng)的评疗。
log如下:
system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms
上面介紹了應(yīng)用啟動(dòng)的基本知識(shí),下面我們倆看看如何定位應(yīng)用啟動(dòng)的元兇茵烈。
利用AndroidStudio
的"Method Tracer tool" 和"Tracer結(jié)合Systracer Tool".關(guān)于Method Tracer tool的使用可以查看官方的文檔MethodTracer.
一般用的比較多的是 Trace和Systrace百匆,因?yàn)椴灰欢梢哉J褂?Method Tracer TOOL".
常見(jiàn)的問(wèn)題
谷歌官方列舉了經(jīng)常會(huì)影響APP啟動(dòng)性能的常見(jiàn)問(wèn)題。
Application初始化負(fù)載
Applcation oncreate 有很多耗時(shí)的操作呜投。比如數(shù)據(jù)庫(kù)訪問(wèn)加匈,IO操作之類(lèi)的。
所以不要在這些方法里面做耗時(shí)的操作仑荐,解決方案下面將講解雕拼。
-
問(wèn)題解決
對(duì)于數(shù)據(jù)的加載,如果不是里面需要的數(shù)據(jù)粘招,可以改成懶加載方式啥寇。對(duì)于靜態(tài)的對(duì)象,可以轉(zhuǎn)換成單例對(duì)象男图,這樣只會(huì)在第一次初始化該對(duì)象示姿。官方建議可以使用
Dagger
來(lái)實(shí)現(xiàn)依賴注入甜橱。這樣只會(huì)在第一次的時(shí)候注入.
Activity初始化負(fù)載
Activity創(chuàng)建的時(shí)候常常做了很多開(kāi)銷(xiāo)很大的工作逊笆。對(duì)于這種情況,有很多可以優(yōu)化的地方岂傲。
- 加載大而復(fù)雜的布局
- 網(wǎng)絡(luò)操作和磁盤(pán)操作堵塞布局繪制
- 解碼加載bitmap
- 柵格化VectorDrawable對(duì)象
- 初始化activity的其他一些子對(duì)象
-
問(wèn)題解決
通過(guò)tracer定位問(wèn)題难裆,然后解決。
- 如果你的布局過(guò)于復(fù)雜就會(huì)加大啟動(dòng)activity的時(shí)間,所以盡量?jī)?yōu)化你的布局
- 除掉冗余和嵌套的布局乃戈,可以通過(guò)lint來(lái)檢測(cè)代碼褂痰,lint會(huì)對(duì)布局進(jìn)行檢測(cè)。
- 不要加載在啟動(dòng)Activity時(shí)症虑,不需要可見(jiàn)的布局缩歪,利用
ViewStub
,通過(guò)這個(gè)可以在適當(dāng)?shù)臅r(shí)間加載布局。
- 在主線程加載所有的資源也會(huì)拖慢加載的速度谍憔。
- 在非主線程加載數(shù)據(jù)匪蝙,懶加載。當(dāng)然在不影響你的需求的情況下习贫,把能異步加載的異步加載逛球。
- 先顯示布局,對(duì)于圖片等的加載可以延遲加載
- Activity oncreate苫昌,onResume颤绕,onstart耗時(shí)過(guò)多,如果還有上一個(gè)Activity祟身,上一個(gè)Activity的onPaues也能有太多耗時(shí)因?yàn)檫@也會(huì)影響APP啟動(dòng)的性能奥务。所以呢不要在這里沒(méi)進(jìn)行不必要的耗時(shí)操作。
不根治但是實(shí)用的解決辦法
通過(guò)設(shè)置theme
來(lái)避免黑白屏袜硫,類(lèi)似于展示一個(gè)閃屏汗洒。等Activity加載好之后就會(huì)顯示Activity。
具體做法:
- 定義一個(gè)Drawable文件父款,然后在
AndroidMainfest.xml
文件里面將主題設(shè)置給對(duì)應(yīng)的Actvity溢谤,最后在該Activity的oncreate方法的setContentView之前將主題設(shè)置回來(lái)。
代碼如下:
drawable示例:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
<!-- The background color, preferably the same as your normal theme -->
<item android:drawable="@android:color/white"/>
<!-- Your product logo - 144dp color version of your app icon -->
<item>
<bitmap
android:src="@drawable/product_logo_144dp"
android:gravity="center"/>
</item>
</layer-list>
Mainfest file:
<activity ...
android:theme="@style/AppTheme.Launcher" />
Activity onCreate示例:
public class MyMainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Make sure this is before calling super.onCreate
setTheme(R.style.Theme_MyApp);
super.onCreate(savedInstanceState);
// ...
}
}