【觸發(fā)背景】面對(duì)海量APP的今天笨鸡,APP用戶量和活躍度成為評(píng)價(jià)一款A(yù)PP是否成功的重要因素狸眼。用戶下載APP后,APP性能體驗(yàn)比如啟動(dòng)時(shí)間谢鹊,直接影響用戶使用頻率算吩,甚至決定是否卸載APP。想和大家分享【APP性能關(guān)于啟動(dòng)時(shí)間】的資料和心得佃扼。
一偎巢、APP啟動(dòng)方式
通常來(lái)說(shuō),APP中啟動(dòng)方式分為兩種:冷啟動(dòng)和熱啟動(dòng)松嘶。
1.冷啟動(dòng):當(dāng)啟動(dòng)應(yīng)用時(shí)艘狭,后臺(tái)沒(méi)有該應(yīng)用的進(jìn)程,這時(shí)系統(tǒng)會(huì)重新創(chuàng)建一個(gè)新的進(jìn)程分配給該應(yīng)用翠订,這個(gè)啟動(dòng)方式就是冷啟動(dòng)巢音。
2.熱啟動(dòng):當(dāng)啟動(dòng)應(yīng)用時(shí),后臺(tái)已有該應(yīng)用的進(jìn)程(例:按back鍵/home鍵尽超,應(yīng)用雖然會(huì)退出官撼,但是該應(yīng)用的進(jìn)程是依然會(huì)保留在后臺(tái),可進(jìn)入任務(wù)列表查看)似谁,所以在已有進(jìn)程的情況下傲绣,這種啟動(dòng)會(huì)從已有的進(jìn)程中來(lái)啟動(dòng)應(yīng)用掠哥,這個(gè)方式叫熱啟動(dòng)。
請(qǐng)注意:上面說(shuō)的啟動(dòng)是點(diǎn)擊app的啟動(dòng)圖標(biāo)來(lái)啟動(dòng)的秃诵,而另外一種方式是進(jìn)入最近使用的列表界面來(lái)啟動(dòng)應(yīng)用续搀,這種不應(yīng)該叫啟動(dòng),應(yīng)該叫恢復(fù)菠净。
以Android系統(tǒng)舉例
冷啟動(dòng):冷啟動(dòng)后系統(tǒng)會(huì)重新創(chuàng)建一個(gè)新的進(jìn)程分配給它禁舷,所以先創(chuàng)建和初始化Application類,再創(chuàng)建和初始化MainActivity類(包括一系列的測(cè)量毅往、布局牵咙、繪制),最后經(jīng)過(guò)渲染顯示在app界面攀唯。
熱啟動(dòng):熱啟動(dòng)因?yàn)闀?huì)從已有的進(jìn)程中來(lái)啟動(dòng)洁桌,所以熱啟動(dòng)就不會(huì)走Application這步了,而是直接走M(jìn)ainActivity(包括一系列的測(cè)量侯嘀、布局另凌、繪制),所以熱啟動(dòng)的過(guò)程只需要?jiǎng)?chuàng)建和初始化一個(gè)MainActivity就行了戒幔,而不必創(chuàng)建和初始化Application途茫,因?yàn)橐粋€(gè)應(yīng)用從新進(jìn)程的創(chuàng)建到進(jìn)程的銷毀,Application只會(huì)初始化一次溪食。
Android系統(tǒng)上,APP無(wú)進(jìn)程狀態(tài)娜扇,啟動(dòng)流程見(jiàn)下:
Application的構(gòu)造器方法 ——>attachBaseContext() ——>onCreate() ——>Activity的構(gòu)造方法 ——>onCreate() ——>配置主題中背景等屬性 ——>onStart() ——>onResume() ——>測(cè)量布局繪制顯示在界面上错沃。
當(dāng)點(diǎn)擊APP的啟動(dòng)圖標(biāo)時(shí),安卓系統(tǒng)會(huì)從Zygote進(jìn)程中fork創(chuàng)建出一個(gè)新的進(jìn)程分配給該應(yīng)用雀瓢,之后會(huì)依次創(chuàng)建和初始化Application類枢析、創(chuàng)建MainActivity類、加載主題樣式Theme中的windowBackground等屬性設(shè)置給MainActivity以及配置Activity層級(jí)上的一些屬性刃麸、再inflate布局醒叁、當(dāng)onCreate/onStart/onResume方法都走完了后最后才進(jìn)行contentView的measure/layout/draw顯示在界面上,所以直到這里泊业,應(yīng)用的第一次啟動(dòng)才算完成把沼,這時(shí)候我們看到的界面也就是所說(shuō)的第一幀。
Android啟動(dòng)流程原理詳情請(qǐng)點(diǎn)擊這里
二吁伺、什么是啟動(dòng)時(shí)間饮睬?
1.冷啟動(dòng)時(shí)間
當(dāng)用戶點(diǎn)擊目標(biāo)app圖標(biāo)的 timepoint到顯示界面第一幀的時(shí)間段(當(dāng)用戶點(diǎn)擊你的app那一刻到系統(tǒng)調(diào)用Activity.onCreate()之間的時(shí)間段)
在這個(gè)時(shí)間段內(nèi),WindowManager會(huì)先加載app主題樣式中的windowBackground做為app的預(yù)覽元素篮奄,然后再真正去加載activity的layout布局捆愁。
2.熱啟動(dòng)時(shí)間
用戶把目標(biāo)app切換至后臺(tái)后割去,點(diǎn)擊app圖標(biāo)的timepoint到顯示界面第一幀的時(shí)間段
3.過(guò)程
首先我們要知道當(dāng)打開(kāi)一個(gè)Activity的時(shí)候發(fā)生了什么,在一個(gè)Activity打開(kāi)時(shí)昼丑,如果該Activity所屬的Application還沒(méi)有啟動(dòng)呻逆,那么系統(tǒng)會(huì)為這個(gè)Activity創(chuàng)建一個(gè)進(jìn)程(每創(chuàng)建一個(gè)進(jìn)程都會(huì)調(diào)用一次Application,所以Application的onCreate()方法可能會(huì)被調(diào)用多次)菩帝,在進(jìn)程的創(chuàng)建和初始化中咖城,勢(shì)必會(huì)消耗一些時(shí)間,在這個(gè)時(shí)間里胁附,WindowManager會(huì)先加載APP里的主題樣式里的窗口背景(windowBackground)作為預(yù)覽元素酒繁,然后才去真正的加載布局。如果加載windowBackground時(shí)間過(guò)長(zhǎng)控妻,而默認(rèn)的背景又是黑色或者白色州袒,這樣會(huì)給用戶造成一種錯(cuò)覺(jué),APP不流暢弓候,影響用戶體驗(yàn)郎哭。
【補(bǔ)充解釋】WindowManager:Windows Manager是一款窗口管理終端,可以遠(yuǎn)程連接到Linux的X桌面進(jìn)行管理菇存,與服務(wù)器端產(chǎn)生一個(gè)session相互通信夸研。詳情請(qǐng)點(diǎn)擊這里
三、使用命令獲得啟動(dòng)時(shí)間
使用命令獲得啟動(dòng)時(shí)間
$adb shell am start -W -n packagename/packageName.MainActivity
1.獲得packagename和入口MainActivity信息
$ aapt dump badging <apk路徑>
搜'package' 'launchable-activity',獲得如下信息
2.獲得packagename依鸥、入口MainActivity亥至,填入命令
3.得到結(jié)果
1)冷啟動(dòng)結(jié)果:
2)熱啟動(dòng)結(jié)果:
4.啟動(dòng)時(shí)間數(shù)據(jù)分析
執(zhí)行成功后將返回三個(gè)測(cè)量到的時(shí)間:
1)ThisTime:一般和TotalTime時(shí)間一樣,除非在應(yīng)用啟動(dòng)時(shí)開(kāi)了一個(gè)透明的Activity預(yù)先處理一些事再顯示出主Activity贱迟,這樣將比TotalTime小姐扮。
2)TotalTime:應(yīng)用的啟動(dòng)時(shí)間,包括創(chuàng)建進(jìn)程+Application初始化+Activity初始化到界面顯示衣吠。
3)WaitTime:一般比TotalTime大點(diǎn)茶敏,包括系統(tǒng)影響的耗時(shí)。
關(guān)于ThisTime/TotalTime/WaitTime的區(qū)別,下面是其解釋:“adb shell am start -W ”的實(shí)現(xiàn)在『frameworks\base\cmds\am\src\com\android\commands\am\Am.java』文件中缚俏。其實(shí)就是跨Binder調(diào)用ActivityManagerService.startActivityAndWait() 接口(后面將ActivityManagerService簡(jiǎn)稱為AMS)惊搏,這個(gè)接口返回的結(jié)果包含上面打印的ThisTime、TotalTime時(shí)間.
- startTime記錄的剛準(zhǔn)備調(diào)用startActivityAndWait()的時(shí)間點(diǎn)
- endTime記錄的是startActivityAndWait()函數(shù)調(diào)用返回的時(shí)間點(diǎn)
- WaitTime = startActivityAndWait()調(diào)用耗時(shí)忧换。
ThisTime恬惯、TotalTime 的計(jì)算在 frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java 文件的 reportLaunchTimeLocked() 函數(shù)中。
- curTime表示該函數(shù)調(diào)用的時(shí)間點(diǎn).
- displayStartTime表示一連串啟動(dòng)Activity中的最后一個(gè)Activity的啟動(dòng)時(shí)間點(diǎn).
- mLaunchStartTime表示一連串啟動(dòng)Activity中第一個(gè)Activity的啟動(dòng)時(shí)間點(diǎn).
其余參考資料亚茬,詳情請(qǐng)點(diǎn)擊這里
四宿崭、競(jìng)品結(jié)果分析
判斷是否存在優(yōu)化必要性:對(duì)比競(jìng)品是否存在明顯延時(shí)。
【競(jìng)品產(chǎn)品】A銀行才写、B銀行葡兑,
【背景】為保證數(shù)據(jù)不被其他app影響奖蔓,重啟手機(jī)后,只開(kāi)啟被測(cè)app
【目的】對(duì)比競(jìng)品讹堤,檢查冷啟動(dòng)時(shí)間
【測(cè)試方法】
1重啟手機(jī)后等2min吆鹤,啟動(dòng)app,觀察冷啟動(dòng)時(shí)間
2kill進(jìn)程洲守,啟動(dòng)app疑务,觀察冷啟動(dòng)時(shí)間
ps: 重啟手機(jī)后立即啟動(dòng)你的App的可能會(huì)受到系統(tǒng)自啟動(dòng)app的影響
輸入命令,查詢結(jié)果:
A銀行
1.重啟手機(jī)后等2min梗醇,啟動(dòng)app知允,觀察冷啟動(dòng)時(shí)間
2.kill進(jìn)程,啟動(dòng)app叙谨,觀察冷啟動(dòng)時(shí)間
B銀行
1.重啟手機(jī)后等2min温鸽,啟動(dòng)APP,觀察冷啟動(dòng)時(shí)間
2.kill進(jìn)程手负,啟動(dòng)APP涤垫,觀察冷啟動(dòng)時(shí)間
提取TotalTime對(duì)比,分析結(jié)果:
1.重啟手機(jī)后等2min竟终,啟動(dòng)app蝠猬,觀察冷啟動(dòng)時(shí)間
A銀行TotalTime:1523ms
B銀行TotalTime:510ms
【結(jié)論】通過(guò)對(duì)比可以看到,重啟手機(jī)后统捶,B銀行冷啟動(dòng)時(shí)間明顯優(yōu)于A銀行冷啟動(dòng)時(shí)間
2.kill進(jìn)程榆芦,啟動(dòng)APP,觀察冷啟動(dòng)時(shí)間
A銀行TotalTime:1319ms
B銀行TotalTime:130ms
【結(jié)論】通過(guò)對(duì)比可以看到喘鸟,kill進(jìn)程歧杏,啟動(dòng)APP,B銀行冷啟動(dòng)時(shí)間明顯優(yōu)于A銀行冷啟動(dòng)時(shí)間
3.重啟手機(jī)和kill進(jìn)程迷守,啟動(dòng)APP
A銀行
重啟手機(jī)TotalTime:1523ms
kill進(jìn)程,啟動(dòng)APPTotalTime:1319ms
B銀行
重啟手機(jī)TotalTime:510ms
kill進(jìn)程旺入,啟動(dòng)APPTotalTime:130ms
【結(jié)論】通過(guò)對(duì)比可以看到兑凿,kill進(jìn)程后啟動(dòng)APP,冷啟動(dòng)時(shí)間更快
五茵瘾、優(yōu)化建議
1.減少應(yīng)用啟動(dòng)時(shí)的耗時(shí)
針對(duì)冷啟動(dòng)時(shí)候的一些耗時(shí)礼华,如上測(cè)得這個(gè)應(yīng)用算是中型的app,在冷啟動(dòng)的時(shí)候耗時(shí)已經(jīng)快xxx ms了拗秘,如果項(xiàng)目再大點(diǎn)在Application中配置了更多的初始化操作圣絮,這樣將可能達(dá)到xx s,這樣每次啟動(dòng)都明顯感覺(jué)延遲雕旨,所以app初始化時(shí)考慮如下操作:消除啟動(dòng)時(shí)的白屏/黑屏
在用戶點(diǎn)擊手機(jī)桌面APP的時(shí)候扮匠,看到的黑屏或者白屏其實(shí)是界面渲染前的第一幀捧请,可將Theme里的windowBackground設(shè)置成希望用戶看到的頁(yè)面:
a 將背景圖設(shè)置成我們APP的Logo圖,作為APP啟動(dòng)的引導(dǎo)棒搜,現(xiàn)在市面上大部分的APP也是這么做的疹蛉。
<style name="AppWelcome" parent="AppTheme">
<item name="android:windowBackground">@mipmap/bg_welcome_start</item>
</style>
b 將背景顏色設(shè)置為透明色,這樣當(dāng)用戶點(diǎn)擊桌面APP圖片的時(shí)候力麸,并不會(huì)"立即"進(jìn)入APP可款,而且在桌面上停留一會(huì),其實(shí)這時(shí)候APP已經(jīng)是啟動(dòng)的了克蚂,只是我們心機(jī)的把Theme里的windowBackground的顏色設(shè)置成透明的闺鲸,用戶體驗(yàn)到延遲也會(huì)覺(jué)得是手機(jī)硬件系統(tǒng)導(dǎo)致的,非APP導(dǎo)致的埃叭,降低用戶對(duì)APP指責(zé)度摸恍。
<style name="Appwelcome" parent="android:Theme.Translucent.NoTitleBar.Fullscreen"/>
ps:透明化這種做法需要注意的一點(diǎn),如果直接把Theme引入Activity游盲,在運(yùn)行的時(shí)候可能會(huì)出現(xiàn)如下異常:
java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
這個(gè)是因?yàn)槭褂昧瞬患嫒莸腡heme误墓,例如Activity繼承了AppCompatActivity,解決方案很簡(jiǎn)單:
- 1)讓其Activity集成Activity而不要集成兼容性的AppCompatActivity
- 2)在onCreate()方法里的super.onCreate(savedInstanceState)之前設(shè)置我們?cè)瓉?lái)APP的Theme
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}
}
上面的2種做法益缎,我們都需要將Theme引入對(duì)應(yīng)的Activity
<activity
android:name=".app.main.MainActivity"
android:theme="@style/AppWelcome"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>