淺談android性能優(yōu)化之啟動(dòng)過(guò)程(冷啟動(dòng)和熱啟動(dòng))
本文介紹了淺談android性能優(yōu)化之啟動(dòng)過(guò)程(冷啟動(dòng)和熱啟動(dòng)) ,分享給大家,具體如下:
一、應(yīng)用的啟動(dòng)方式
通常來(lái)說(shuō),啟動(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)。
特點(diǎn)
1、冷啟動(dòng):冷啟動(dòng)因?yàn)橄到y(tǒng)會(huì)重新創(chuàng)建一個(gè)新的進(jìn)程分配給它,所以會(huì)先創(chuàng)建和初始化Application類,再創(chuàng)建和初始化MainActivity類(包括一系列的測(cè)量、布局、繪制),最后顯示在界面上。
2、熱啟動(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ì)初始化一次。
二、應(yīng)用的啟動(dòng)過(guò)程
冷啟動(dòng)啟動(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)用,之后會(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ō)的第一幀。所以,總結(jié)一下,應(yīng)用的啟動(dòng)流程如下:
Application的構(gòu)造器方法——>attachBaseContext()——>onCreate()——>Activity的構(gòu)造方法——>onCreate()——>配置主題中背景等屬性——>onStart()——>onResume()——>測(cè)量布局繪制顯示在界面上。
大致流程如下:
1、點(diǎn)擊桌面圖標(biāo),Launcher會(huì)啟動(dòng)程序默認(rèn)的Acticity,之后再按照程序的邏輯啟動(dòng)各種Activity
2、啟動(dòng)Activity都需要借助應(yīng)用程序框架層的ActivityManagerService服務(wù)進(jìn)程(Service也是由ActivityManagerService進(jìn)程來(lái)啟動(dòng)的);在Android應(yīng)用程序框架層中,ActivityManagerService是一個(gè)非常重要的接口,
它不但負(fù)責(zé)啟動(dòng)Activity和Service,還負(fù)責(zé)管理Activity和Service。
Step 1. 無(wú)論是通過(guò)Launcher來(lái)啟動(dòng)Activity,還是通過(guò)Activity內(nèi)部調(diào)用startActivity接口來(lái)啟動(dòng)新的Activity,都通過(guò)Binder進(jìn)程間通信進(jìn)入到ActivityManagerService進(jìn)程中,并且調(diào)用ActivityManagerService.startActivity接口;
Step 2. ActivityManagerService調(diào)用ActivityStack.startActivityMayWait來(lái)做準(zhǔn)備要啟動(dòng)的Activity的相關(guān)信息;
Step 3. ActivityStack通知ApplicationThread要進(jìn)行Activity啟動(dòng)調(diào)度了,這里的ApplicationThread代表的是調(diào)用ActivityManagerService.startActivity接口的進(jìn)程,對(duì)于通過(guò)點(diǎn)擊應(yīng)用程序圖標(biāo)的情景來(lái)說(shuō),這個(gè)進(jìn)程就是Launcher了,
而對(duì)于通過(guò)在Activity內(nèi)部調(diào)用startActivity的情景來(lái)說(shuō),這個(gè)進(jìn)程就是這個(gè)Activity所在的進(jìn)程了;
Step 4. ApplicationThread不執(zhí)行真正的啟動(dòng)操作,它通過(guò)調(diào)用ActivityManagerService.activityPaused接口進(jìn)入到ActivityManagerService進(jìn)程中,看看是否需要?jiǎng)?chuàng)建新的進(jìn)程來(lái)啟動(dòng)Activity;
Step 5. 對(duì)于通過(guò)點(diǎn)擊應(yīng)用程序圖標(biāo)來(lái)啟動(dòng)Activity的情景來(lái)說(shuō),ActivityManagerService在這一步中,會(huì)調(diào)用startProcessLocked來(lái)創(chuàng)建一個(gè)新的進(jìn)程,而對(duì)于通過(guò)在Activity內(nèi)部調(diào)用startActivity來(lái)啟動(dòng)新的Activity來(lái)說(shuō),這一步是不需要執(zhí)行的,
因?yàn)樾碌腁ctivity就在原來(lái)的Activity所在的進(jìn)程中進(jìn)行啟動(dòng);
Step 6. ActivityManagerServic調(diào)用ApplicationThread.scheduleLaunchActivity接口,通知相應(yīng)的進(jìn)程執(zhí)行啟動(dòng)Activity的操作;
Step 7. ApplicationThread把這個(gè)啟動(dòng)Activity的操作轉(zhuǎn)發(fā)給ActivityThread,ActivityThread通過(guò)ClassLoader導(dǎo)入相應(yīng)的Activity類,然后把它啟動(dòng)起來(lái)。
三、冷啟動(dòng)過(guò)程中碰到的白屏黑屏以及優(yōu)化啟動(dòng)時(shí)間
1、白屏問(wèn)題 :
android studio升級(jí) 2.0之后 加上Instant Run,Instant Run為了能夠讓我們快速部署代碼,背后其實(shí)是有一套非常復(fù)雜的邏輯的,比如要在APK中建立服務(wù)器與Android Studio進(jìn)行通信,以及代碼差異比對(duì)和替換等,在研發(fā)過(guò)程中可能出現(xiàn)白屏問(wèn)題,
一般release版的程序是不會(huì)出現(xiàn)這種現(xiàn)象的;
如果接下來(lái)還會(huì)出現(xiàn)白屏問(wèn)題,可以查看style文件
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> ...... <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> </style>
加入了兩個(gè)屬性,windowIsTranslucent和windowNoTitle,將這兩個(gè)屬性都設(shè)置成true,就可以讓程序在初始化的時(shí)候窗口是透明的,初始化結(jié)束后程序主界面才會(huì)顯示出來(lái),從而也就完全看不到白屏界面了
2、啟動(dòng)時(shí)間的優(yōu)化
先測(cè)量activity的啟動(dòng)時(shí)間-------Activity的reportFullyDrawn()方法
你就需要調(diào)用Activity的reportFullyDrawn()。它將在log里報(bào)告從apk初始化(和前面Displayed的時(shí)間是一樣的)到reportFullyDrawn() 方法被調(diào)用用了多長(zhǎng)時(shí)間。
reportFullyDrawn()方法顯示的log也是類似這樣:
ActivityManager: Displayed com.Android.myexample/.StartupTiming: +768ms
在4.4上調(diào)用reportFullyDrawn()方法會(huì)崩潰(但是log還是能正常打?。?,提示需要UPDATE_DEVICE_STATS權(quán)限 ,但是這個(gè)權(quán)限只有系統(tǒng)app才能授權(quán)。解決的辦法是這樣調(diào)
try{ reportFullyDrawn(); }catch(SecurityException e){ }
還有一種測(cè)量啟動(dòng)時(shí)間的方法也值得一提,那就是screenrecord命令
首先啟動(dòng)帶—bugreport選項(xiàng)(它可以在frames 中添加時(shí)間戳-應(yīng)該是L中的特性)的screenrecord 命令:
$ adb shell screenrecord --bugreport /sdcard/launch.mp4
然后點(diǎn)擊app的圖標(biāo),等待app顯示,ctrl-C screenrecord, 使用adb pull命令把文件導(dǎo)出到電腦。
$ adb pull /sdcard/launch.mp4
現(xiàn)在你可以打開錄制視頻看看發(fā)生了什么。你需要一個(gè)能逐幀查看的視頻播放器(mac上的Quicktime 就可以,不清楚其它os上什么播放器這個(gè)功能最好使)?,F(xiàn)在逐幀播放,注意視頻的上方有一個(gè)frame 時(shí)間戳。
一直往前直到你發(fā)現(xiàn)app圖標(biāo)高亮了為止。這個(gè)時(shí)候系統(tǒng)已經(jīng)處理了圖標(biāo)上的點(diǎn)擊事件,開始啟動(dòng)app了,記錄下這一幀的時(shí)間。繼續(xù)播放幀直到你看到了app整個(gè)UI的第一幀為止。根據(jù)不同情況(是否有啟動(dòng)窗口,是否有啟動(dòng)畫面等等),
事件和窗口發(fā)生的實(shí)際順序可能會(huì)有不同。對(duì)于一個(gè)簡(jiǎn)單的app來(lái)說(shuō),你會(huì)首先見到啟動(dòng)窗口,然后漸變出app真實(shí)的UI。在你看到UI上的任何內(nèi)容之后,你應(yīng)該記錄下第一幀,這時(shí)app完成了布局和繪制,準(zhǔn)備開始顯示出來(lái)了。同時(shí)也記錄下這一幀所發(fā)生的時(shí)間。
現(xiàn)在把這兩個(gè)時(shí)間相減 ((UI displayed) - (icon tapped)); 得到app從點(diǎn)擊到繪制就緒的所有時(shí)間。雖然這個(gè)時(shí)間包含了進(jìn)程啟動(dòng)之前的時(shí)間,但是至少它可以用于跟其他app比較。
Android冷啟動(dòng)時(shí)間優(yōu)化
冷啟動(dòng)時(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布局
冷啟動(dòng)時(shí)間優(yōu)化
知道了Android冷啟動(dòng)時(shí)間的原理之后,就可以通過(guò)一些小技巧來(lái)對(duì)冷啟動(dòng)時(shí)間進(jìn)行優(yōu)化,從而讓你app加載變得”快“一些(視覺(jué)體驗(yàn)上的快)。我們可制作一個(gè)啟動(dòng)Activity的背景樣式的.9圖片,然后把這個(gè).9圖片做為windowBackground。
圖片制作好之后,我們就可以用它做為app冷啟動(dòng)階段的預(yù)覽元素,如下設(shè)置:
為啟動(dòng)的Activity自定義一個(gè)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è)置了一個(gè)新的Theme,這樣做會(huì)覆蓋原來(lái)的Theme,所以在MainActivity中需要設(shè)置回原來(lái)的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); } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Android10的分區(qū)存儲(chǔ)機(jī)制(Scoped Storage)適配教程
這篇文章主要介紹了詳解Android10的分區(qū)存儲(chǔ)機(jī)制(Scoped Storage)適配教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Android中封裝SDK時(shí)常用的注解總結(jié)
這篇文章主要給大家總結(jié)了在Android中封裝SDK時(shí)常用的注解的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-05-05Android ListView 實(shí)現(xiàn)上拉加載的示例代碼
這篇文章主要介紹了Android ListView 實(shí)現(xiàn)上拉加載的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Android編程實(shí)現(xiàn)分頁(yè)加載ListView功能示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)分頁(yè)加載ListView功能,結(jié)合實(shí)例形式分析了listview分頁(yè)加載的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-02-02Flutter質(zhì)感設(shè)計(jì)之彈出菜單
這篇文章主要為大家詳細(xì)介紹了Flutter質(zhì)感設(shè)計(jì)之彈出菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08