啟動(dòng)是一個(gè)什么樣的過程雪标?首先要對(duì)這個(gè)過程進(jìn)行一個(gè)定義∫楹觯考慮到進(jìn)程是否存在懒闷,對(duì)啟動(dòng)時(shí)間有著明確的影響,將啟動(dòng)分為兩種情況栈幸。
冷啟動(dòng) :在進(jìn)程不存在的情況下愤估,從點(diǎn)擊應(yīng)用 Icon 到用戶能看到界面所占用的時(shí)間。
熱啟動(dòng) :在進(jìn)程依然存在的情況下速址,點(diǎn)擊應(yīng)用 Icon 到用戶能看到對(duì)應(yīng)的界面所用的時(shí)間玩焰。
知道這兩個(gè)啟動(dòng)時(shí)間怎么定義后,需要將這個(gè)進(jìn)行實(shí)際的測(cè)量芍锚。通過這個(gè)測(cè)量時(shí)間的獲得昔园,就能定性地去衡量啟動(dòng)過程的性能問題。性能越好闹炉,所有時(shí)間就會(huì)更少蒿赢,反之就更多。
宏觀測(cè)量 — Adb 命令的使用
那么接下來看看渣触,具體怎么將這個(gè)測(cè)量過程落地羡棵。
Android adb 提供了很簡(jiǎn)單的測(cè)量方式,預(yù)先通過在 FrameWork 層中的 ActivityManagerService 等等中進(jìn)行埋點(diǎn)的方式嗅钻,這樣可以獲得對(duì)應(yīng)的時(shí)間點(diǎn)皂冰。
adb shell am start -W [packageName]/[activityName]
這樣執(zhí)行后,可以得到如下圖所示的樣子养篓。
這里面涉及到三個(gè)時(shí)間秃流,ThisTime、TotalTime 和 WaitTime柳弄。WaitTime 是 startActivityAndWait 這個(gè)方法的調(diào)用耗時(shí)舶胀,ThisTime 是指調(diào)用過程中最后一個(gè) Activity 啟動(dòng)時(shí)間到這個(gè) Activity 的 startActivityAndWait 調(diào)用結(jié)束。TotalTime 是指調(diào)用過程中第一個(gè) Activity 的啟動(dòng)時(shí)間到最后一個(gè) Activity 的 startActivityAndWait 結(jié)束碧注。如果過程中只有一個(gè) Activity 嚣伐,則 TotalTime 等于 ThisTime。
final long startTime = SystemClock.uptimeMillis();
if (mWaitOption) {
result = mAm.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo, null, mUserId);
res = result.result;
} else {
res = mAm.startActivityAsUser(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo, null, mUserId);
}
final long endTime = SystemClock.uptimeMillis();
上面是 WaitTime 的計(jì)算方式萍丐,其他時(shí)間的計(jì)算方式也請(qǐng)參考源碼轩端。不過就實(shí)際的使用而言,這種方式得出的時(shí)間逝变,不夠準(zhǔn)確基茵,誤差較大奋构。
在 API 19 后,ActivityManagerService 也通過日志的方式告訴我們 Activity 啟動(dòng)花了多少時(shí)間拱层。
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
這個(gè)時(shí)間也可以作為高版本系統(tǒng)中的另一個(gè)宏觀參考指標(biāo)弥臼。
使用這種方式的話,不如在關(guān)鍵時(shí)間點(diǎn)記錄相關(guān)的時(shí)間舱呻,這種方式會(huì)更加直觀一點(diǎn)醋火,粒度也會(huì)更加精細(xì)一些。
精確測(cè)量 — TraceView 的應(yīng)用
大部分人都知道利用 TraceView 進(jìn)行分析箱吕,通過 TraceView 提供的數(shù)據(jù)分析出對(duì)于哪些方法是造成耗時(shí)的緣故芥驳。這里還是要簡(jiǎn)單地介紹一下 TraceView,方便不了解的同學(xué)茬高。
TraceView 位于 Android Devices Monitor 里面兆旬。可以在 Android Studio -》Tools -》Android 中找到 Devices Monitor怎栽。在 Monitor 中就可以打開對(duì)應(yīng)的 Trace 文件丽猬,或者在這里進(jìn)行 Trace 操作。下圖展示的是一個(gè) Trace 界面熏瞄。
TraceView 關(guān)鍵指標(biāo)的講解
在上圖中的下部分脚祟,是每個(gè)函數(shù)相關(guān)的調(diào)用信息。左邊是函數(shù)的名稱强饮,右邊是其調(diào)用的各種統(tǒng)計(jì)信息由桌。
Incl Cpu Time: 這個(gè)方法調(diào)用所占據(jù)的時(shí)間。
Excl Cpu Time: 這個(gè)方法本身執(zhí)行的時(shí)間邮丰,例如這個(gè)方法里面調(diào)用了 a, b 兩個(gè)方法行您,Excl 就是排除 a,b 兩者占用的時(shí)間之后的時(shí)間剪廉。
Incl Real Time 等等與前者類似娃循,一個(gè)是 Real time,一個(gè)是 CPU 時(shí)間斗蒋,兩個(gè)區(qū)別不大捌斧。
Calls + Recur Calls / Total: 函數(shù)的調(diào)用次數(shù)(包含了遞歸調(diào)用) / 總次數(shù)。通常用于判斷一些方法是否出現(xiàn)這個(gè)異常泉沾。
Cpu Time / Call: 每個(gè)函數(shù)的平均調(diào)用時(shí)間捞蚂,這個(gè)對(duì)一些常用方法的判斷尤為重要。例如一個(gè) RecyclerView 經(jīng)常調(diào)用 getview 方法爆哑,那么我對(duì)這個(gè)方法里面的優(yōu)化是否生效呢?顯然不能看單個(gè) getview 的耗時(shí)舆吮,而是平均每個(gè) getview 方法的耗時(shí)揭朝。
TraceView 的粗略使用指南
在 Android Devices Monitor 中左側(cè)選中對(duì)應(yīng)的進(jìn)程名队贱,然后點(diǎn)擊如箭頭所指的按鈕,按鈕中紅點(diǎn)將會(huì)變成黑點(diǎn)潭袱,在界面上進(jìn)行相應(yīng)的操作柱嫌,然后再次點(diǎn)擊該按鈕。等待片刻后屯换,界面上就會(huì)出現(xiàn)相應(yīng)的 TraceView 界面编丘,根據(jù)上文所述的指標(biāo)意義,找出問題的癥結(jié)彤悔。
如果是針對(duì)啟動(dòng)問題的話嘉抓,使用這種方式就不太合理了,還沒等你點(diǎn)擊按鈕晕窑,就已經(jīng)執(zhí)行過了抑片。給大家一種技巧,
// 在 /sdcard 目錄下記錄 trace_file.trace 的文件, 開始 trace
Debug.startMethodTracing("trace_file");
// 結(jié)束 trace杨赤,并輸出文件
Debug.stopMethodTracing();
在這種情況下敞斋,只要在啟動(dòng)的前后分別調(diào)用兩個(gè)方法,就能在 sdcard 中找到對(duì)應(yīng)的 trace 文件疾牲。后續(xù)再使用 adb pull 導(dǎo)出植捎,并使用 Android Studio ,或者 Devices Monitor 打開文件阳柔,就能進(jìn)行分析了焰枢。
官方的偷天換日
對(duì)于大型 APK 而言,優(yōu)化并非那么簡(jiǎn)單盔沫,牽涉到很多模塊医咨,某些模塊的初始化必須在此啟動(dòng),在這種情況下架诞,Google 官方教程推出了 Launch-Time Performance | Android Developers 這篇文章拟淮。這里簡(jiǎn)單地介紹下這種偷天換日的方案,讓用戶在體驗(yàn)上感覺應(yīng)用快了那么一點(diǎn)點(diǎn)谴忧。
在啟動(dòng) Activity 之間很泊,系統(tǒng)會(huì)先生成一個(gè)空白的 Window,等到 Activity 的各類資源準(zhǔn)備完畢后沾谓,將其放置到 Window 上去委造。偷天換日的方案就是在這個(gè) window 上做手腳,先將 window 的背景替換成類似于主界面的背景均驶,這樣用戶會(huì)以為此時(shí)界面已經(jīng)在加載中了昏兆。等一段時(shí)間后,再將這個(gè) Window 替換為實(shí)際的 Activity 界面妇穴。
首先指定一個(gè)背景圖爬虱,就像下面的代碼一樣隶债。
<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>
再指定 Activity 的主題到這個(gè) style 文件中。
<activity ...
android:theme="@style/AppTheme.Launcher" />
最后跑筝,一定要記得在 Activity 的 onCreate 方法里進(jìn)行 Style 替換死讹。
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);
...
}
}
最好的方式還是優(yōu)化代碼結(jié)構(gòu),一勞永逸地解決問題曲梗,這樣才能自豪地說赞警,我就是快!
文檔信息
- 版權(quán)聲明:自由轉(zhuǎn)載-非商用-非衍生-保持署名(創(chuàng)意共享3.0許可證)
- 發(fā)表日期:2016年11月9日