最近一直想整理一個關(guān)于Android熱啟動狰域,冷啟動的文章,于是就有了下文(本文僅僅只是整理總結(jié)前人的知識點)
來源:http://www.cnblogs.com/xunzhi/p/5794793.html
來源:http://blog.csdn.net/growing_tree/article/details/53183511
首先簡單說下這兩種啟動方式
冷啟動:
當(dāng)啟動應(yīng)用時谷炸,后臺沒有該應(yīng)用的進程北专,這時系統(tǒng)會重新創(chuàng)建一個新的進程分配給該應(yīng)用,這個啟動方式就是冷啟動旬陡,也就是先實例化Application拓颓。
熱啟動:
當(dāng)啟動應(yīng)用時,后臺已有該應(yīng)用的進程(例:按back鍵描孟、home鍵驶睦,應(yīng)用雖然會退出砰左,但是該應(yīng)用的進程是依然會保留在后臺,可進入任務(wù)列表查看)场航,所以在已有進程的情況下缠导,這種啟動會從已有的進程中來啟動應(yīng)用,也就是直接從進程中啟動,不需要重新創(chuàng)建Application溉痢,這個方式叫熱啟動僻造。
應(yīng)用的啟動過程
冷啟動啟動流程:當(dāng)點擊app的啟動圖標時,安卓系統(tǒng)會從Zygote進程中fork創(chuàng)建出一個新的進程分配給該應(yīng)用孩饼,之后會依次創(chuàng)建和初始化Application類髓削、創(chuàng)建MainActivity類、加載主題樣式Theme中的windowBackground等屬性設(shè)置給MainActivity以及配置Activity層級上的一些屬性镀娶、再inflate布局立膛、當(dāng)onCreate/onStart/onResume方法都走完了后最后才進行contentView的measure/layout/draw顯示在界面上,所以直到這里梯码,應(yīng)用的第一次啟動才算完成宝泵,這時候我們看到的界面也就是所說的第一幀。所以轩娶,總結(jié)一下儿奶,應(yīng)用的啟動流程如下:
Application的構(gòu)造器方法——>attachBaseContext()——>onCreate()——>Activity的構(gòu)造方法——>onCreate()——>配置主題中背景等屬性——>onStart()——>onResume()——>測量布局繪制顯示在界面上。
大致流程如下:
1罢坝、點擊桌面圖標廓握,Launcher會啟動程序默認的Acticity,之后再按照程序的邏輯啟Activity
2嘁酿、啟動Activity都需要借助應(yīng)用程序框架層的ActivityManagerService服務(wù)進程(Service也是由ActivityManagerService進程來啟動的)
Step 1. 無論是通過Launcher來啟動Activity隙券,還是通過Activity內(nèi)部調(diào)用startActivity接口來啟動新的Activity,都通過Binder進程間通信進入到ActivityManagerService進程中闹司,并且調(diào)用ActivityManagerService.startActivity接口娱仔;
Step 2. ActivityManagerService調(diào)用ActivityStack.startActivityMayWait來做準備要啟動的Activity的相關(guān)信息;
Step 3. ActivityStack通知ApplicationThread要進行Activity啟動調(diào)度了游桩,這里的ApplicationThread代表的是調(diào)用ActivityManagerService.startActivity接口的進程牲迫,對于通過點擊應(yīng)用程序圖標的情景來說,這個進程就是Launcher了借卧,而對于通過在Activity內(nèi)部調(diào)用startActivity的情景來說盹憎,這個進程就是這個Activity所在的進程了;
Step 4. ApplicationThread不執(zhí)行真正的啟動操作铐刘,它通過調(diào)用ActivityManagerService.activityPaused接口進入到ActivityManagerService進程中陪每,看看是否需要創(chuàng)建新的進程來啟動Activity;
Step 5. 對于通過點擊應(yīng)用程序圖標來啟動Activity的情景來說,ActivityManagerService在這一步中檩禾,會調(diào)用startProcessLocked來創(chuàng)建一個新的進程挂签,而對于通過在Activity內(nèi)部調(diào)用startActivity來啟動新的Activity來說,這一步是不需要執(zhí)行的盼产,因為新的Activity就在原來的Activity所在的進程中進行啟動饵婆;
Step 6. ActivityManagerServic調(diào)用ApplicationThread.scheduleLaunchActivity接口,通知相應(yīng)的進程執(zhí)行啟動Activity的操作戏售;
Step 7. ApplicationThread把這個啟動Activity的操作轉(zhuǎn)發(fā)給ActivityThread侨核,ActivityThread通過ClassLoader導(dǎo)入相應(yīng)的Activity類,然后把它啟動起來蜈项。
冷啟動過程中碰到的白屏黑屏以及優(yōu)化啟動時間
1芹关、白屏問題 :
android studio升級 2.0之后 加上Instant Run续挟,Instant Run為了能夠讓我們快速部署代碼紧卒,背后其實是有一套非常復(fù)雜的邏輯的,比如要在APK中建立服務(wù)器與Android Studio進行通信诗祸,以及代碼差異比對和替換等跑芳,在研發(fā)過程中可能出現(xiàn)白屏問題,一般release版的程序是不會出現(xiàn)這種現(xiàn)象的直颅;如果接下來還會出現(xiàn)白屏問題博个,可以查看style文件
1<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> ...... <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> </style>
加入了兩個屬性,windowIsTranslucent和windowNoTitle功偿,將這兩個屬性都設(shè)置成true盆佣,就可以讓程序在初始化的時候窗口是透明的,初始化結(jié)束后程序主界面才會顯示出來械荷,從而也就完全看不到白屏界面了
2共耍、啟動時間的優(yōu)化
先測量activity的啟動時間-------Activity的reportFullyDrawn()方法
你就需要調(diào)用Activity的reportFullyDrawn()。它將在log里報告從apk初始化(和前面Displayed的時間是一樣的)到reportFullyDrawn() 方法被調(diào)用用了多長時間吨瞎。
reportFullyDrawn()方法顯示的log也是類似這樣:
ActivityManager: Displayed com.Android.myexample/.StartupTiming: +768ms
在4.4上調(diào)用reportFullyDrawn()方法會崩潰(但是log還是能正常打颖远怠),提示需要UPDATE_DEVICE_STATS權(quán)限 颤诀,但是這個權(quán)限只有系統(tǒng)app才能授權(quán)字旭。解決的辦法是這樣調(diào)
try{
reportFullyDrawn();
}
catch(SecurityException e){}
還有一種測量啟動時間的方法也值得一提,那就是screenrecord命令
首先啟動帶—bugreport選項(它可以在frames 中添加時間戳-應(yīng)該是L中的特性)的screenrecord 命令:
$ adb shell screenrecord --bugreport /sdcard/launch.mp4 然后點擊app的圖標崖叫,等待app顯示遗淳,ctrl-C screenrecord, 使用adb pull命令把文件導(dǎo)出到電腦。
$ adb pull /sdcard/launch.mp4
現(xiàn)在你可以打開錄制視頻看看發(fā)生了什么心傀。你需要一個能逐幀查看的視頻播放器(mac上的Quicktime 就可以屈暗,不清楚其它os上什么播放器這個功能最好使)。現(xiàn)在逐幀播放,注意視頻的上方有一個frame 時間戳恐锦。
一直往前直到你發(fā)現(xiàn)app圖標高亮了為止往果。這個時候系統(tǒng)已經(jīng)處理了圖標上的點擊事件,開始啟動app了一铅,記錄下這一幀的時間陕贮。繼續(xù)播放幀直到你看到了app整個UI的第一幀為止。根據(jù)不同情況(是否有啟動窗口潘飘,是否有啟動畫面等等)肮之,
事件和窗口發(fā)生的實際順序可能會有不同。對于一個簡單的app來說卜录,你會首先見到啟動窗口戈擒,然后漸變出app真實的UI。在你看到UI上的任何內(nèi)容之后艰毒,你應(yīng)該記錄下第一幀筐高,這時app完成了布局和繪制,準備開始顯示出來了丑瞧。同時也記錄下這一幀所發(fā)生的時間柑土。
現(xiàn)在把這兩個時間相減 ((UI displayed) - (icon tapped)); 得到app從點擊到繪制就緒的所有時間。雖然這個時間包含了進程啟動之前的時間绊汹,但是至少它可以用于跟其他app比較稽屏。
Android冷啟動時間優(yōu)化
冷啟動時間是指當(dāng)用戶點擊你的app那一刻到系統(tǒng)調(diào)用Activity.onCreate()之間的時間段。在這個時間段內(nèi)西乖,WindowManager會先加載app主題樣式中的windowBackground做為app的預(yù)覽元素狐榔,然后再真正去加載activity的layout布局
冷啟動時間優(yōu)化
知道了Android冷啟動時間的原理之后,就可以通過一些小技巧來對冷啟動時間進行優(yōu)化获雕,從而讓你app加載變得”快“一些(視覺體驗上的快)薄腻。我們可制作一個啟動Activity的背景樣式的.9圖片,然后把這個.9圖片做為windowBackground典鸡。
圖片制作好之后被廓,我們就可以用它做為app冷啟動階段的預(yù)覽元素,如下設(shè)置:
為啟動的Activity自定義一個Theme
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/window_background_statusbar_toolbar_tab</item>
</style>
將新的Theme應(yīng)用到設(shè)置到AndroidManifest.xml中
<activity
android:name=".MainActivity"
android:theme="@style/AppTheme.Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
由于給MainActivity設(shè)置了一個新的Theme萝玷,這樣做會覆蓋原來的Theme嫁乘,所以在MainActivity中需要設(shè)置回原來的Theme
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { // Make sure this line comes before calling super.onCreate(). setTheme(R.style.AppTheme); super.onCreate(savedInstanceState); }}