當(dāng)系統(tǒng)啟動(dòng)一個(gè)App時(shí)柄冲,zygote進(jìn)程會(huì)首先創(chuàng)建一個(gè)新的進(jìn)程去運(yùn)行這個(gè)App放典,但是進(jìn)程的創(chuàng)建是需要時(shí)間的,在創(chuàng)建完成之前巡社,界面是呈現(xiàn)假死狀態(tài)的膛堤,這極大地降低了用戶體驗(yàn),Android需要及時(shí)做出反饋去避免這段迷之尷尬晌该。于是系統(tǒng)根據(jù)你的Manifest文件設(shè)置的主題顏色的不同來(lái)展示一個(gè)白屏(黑屏)肥荔。而這個(gè)白屏(黑屏)正式的稱呼就是Preview Window,即預(yù)覽窗口朝群。這篇文章主要剖析App冷啟動(dòng)時(shí)的兩個(gè)問(wèn)題:
- App啟動(dòng)時(shí)白屏(黑屏)
-
App啟動(dòng)速度慢燕耿,如何點(diǎn)擊秒開
啟動(dòng)黑屏白屏
APP啟動(dòng)時(shí)白屏(黑屏)
Activity如何繪制
首先要說(shuō)明的是無(wú)論App啟動(dòng),還是startActivity
都是Activity的啟動(dòng)姜胖,所以這歸根結(jié)底是一個(gè)問(wèn)題誉帅,究其原因是對(duì)Activity的啟動(dòng)機(jī)制不太了解。Activity啟動(dòng)時(shí)繪制整個(gè)窗口需要按順序執(zhí)行以下幾個(gè)步驟:
- 繪制背景
- 繪制View本身的內(nèi)容
- 繪制子View
- 繪制修飾內(nèi)容(例如滾動(dòng)條)
閃屏原因剖析Preview Window
我們正常開發(fā)中會(huì)在Activity的onCreate()方法中調(diào)用setContentView(View)設(shè)置該Activity的顯示布局右莱,那么問(wèn)題就來(lái)了蚜锨,既然我們?cè)O(shè)置了布局,為什么啟動(dòng)的時(shí)候還會(huì)白屏或者黑屏而不是顯示我set的布局呢慢蜓?下面就帶領(lǐng)大家一起來(lái)剖析一下原因亚再。
當(dāng)打開一個(gè)Activity時(shí),如果這個(gè)Activity所屬Application還沒(méi)有在運(yùn)行晨抡,系統(tǒng)會(huì)為這個(gè)Activity的創(chuàng)建一個(gè)進(jìn)程针余,但進(jìn)程的創(chuàng)建與初始化都需要時(shí)間饲鄙,在這個(gè)動(dòng)作完成之前,如果初始化的時(shí)間過(guò)長(zhǎng)圆雁,屏幕上可能沒(méi)有任何動(dòng)靜忍级,用戶會(huì)以為沒(méi)有點(diǎn)到按鈕。所以既不能停在原來(lái)的地方又沒(méi)到顯示新的界面伪朽,怎么辦呢轴咱?這就有了StartingWindow(也稱之為Preview Window)的出現(xiàn),這樣看起來(lái)就像Activity已經(jīng)啟動(dòng)起來(lái)了烈涮,只是數(shù)據(jù)內(nèi)容還沒(méi)有初始化好朴肺。
StartingWindow一般出現(xiàn)在應(yīng)用程序進(jìn)程創(chuàng)建并初始化成功前,所以它是個(gè)臨時(shí)窗口坚洽,對(duì)應(yīng)的WindowType是TYPE_APPLICATION_STARTING戈稿。目的是告訴用戶,系統(tǒng)已經(jīng)接受到操作讶舰,正在響應(yīng)鞍盗,在程序初始化完成后實(shí)現(xiàn)目標(biāo)UI,同時(shí)移除這個(gè)窗口跳昼。
這個(gè)StartingWindow就是我們要討論的白屏(黑屏)的般甲,一般情況下我們會(huì)對(duì)Application和Activity設(shè)置theme,系統(tǒng)會(huì)根據(jù)設(shè)置的theme初始化StartingWindow鹅颊。Window布局的頂層是DecorView敷存,StartingWindow顯示一個(gè)空DecorView,但是會(huì)給這個(gè)DecorView應(yīng)用這個(gè)Activity指定的theme堪伍,如果這個(gè)Activity沒(méi)有指定theme就用Application的锚烦。
在theme中可以指定窗口的背景、Activity的Icon帝雇、App整體文字顏色等涮俄,如果說(shuō)沒(méi)有指定任何屬性,就會(huì)用默認(rèn)的屬性摊求,也就是上文中提到的空DecorView,所以我們的白屏(黑屏)和空DecorView息息相關(guān)刘离,我們給Application設(shè)置的Style就決定了是白屏還是黑屏室叉。
1、如果選擇了Black系列的主題那么Activity跳轉(zhuǎn)的時(shí)候就是黑屏:
@android:style/Theme.Black
2硫惕、如果選擇了Light系列的主題那么Activity跳轉(zhuǎn)的時(shí)候就是白屏:
@android:style/Theme.Light
3茧痕、常見(jiàn)Theme主題
android:theme="@android:style/Theme.Dialog" // Activity顯示為對(duì)話框模式
android:theme="@android:style/Theme.NoTitleBar" // 不顯示應(yīng)用程序標(biāo)題欄=
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" // 不顯示應(yīng)用程序標(biāo)題欄,并全屏
android:theme="Theme.Light " // 背景為白色
android:theme="Theme.Light.NoTitleBar" // 白色背景并無(wú)標(biāo)題欄
android:theme="Theme.Light.NoTitleBar.Fullscreen" // 白色背景恼除,無(wú)標(biāo)題欄踪旷,全屏
android:theme="Theme.Black" // 背景黑色
android:theme="Theme.Black.NoTitleBar" // 黑色背景并無(wú)標(biāo)題欄
android:theme="Theme.Black.NoTitleBar.Fullscreen" // 黑色背景曼氛,無(wú)標(biāo)題欄,全屏
android:theme="Theme.Wallpaper" // 用系統(tǒng)桌面為應(yīng)用程序背景
android:theme="Theme.Wallpaper.NoTitleBar" // 用系統(tǒng)桌面為應(yīng)用程序背景令野,且無(wú)標(biāo)題欄
android:theme="Theme.Wallpaper.NoTitleBar.Fullscreen" // 用系統(tǒng)桌面為應(yīng)用程序背景舀患,無(wú)標(biāo)題欄,全屏
android:theme="Theme.Translucent" // 透明背景
android:theme="Theme.Translucent.NoTitleBar" // 透明背景并無(wú)標(biāo)題
android:theme="Theme.Translucent.NoTitleBar.Fullscreen" // 透明背景并無(wú)標(biāo)題气破,全屏
android:theme="Theme.Panel " // 面板風(fēng)格顯示
android:theme="Theme.Light.Panel" // 平板風(fēng)格顯示
解決辦法
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">true</item>
</style>
如上設(shè)置后App和Activity啟動(dòng)時(shí)聊浅,我們的StartingWindow會(huì)應(yīng)用我們這個(gè)透明背景的主題,跳轉(zhuǎn)時(shí)確實(shí)沒(méi)有白屏和黑屏了现使,但是這樣設(shè)置會(huì)產(chǎn)生如下后果:
- 給SplashActivity設(shè)置透明Theme后低匙,用戶點(diǎn)擊我們App圖標(biāo)后,需要等待2秒左右的時(shí)候才會(huì)顯示contentView碳锈。造成了App啟動(dòng)速度慢的假象顽冶,其實(shí)Activity已經(jīng)啟動(dòng)了,只是background是透明的售碳,這時(shí)候你點(diǎn)擊桌面的其他地方是無(wú)效的强重。
- 給其他Activity設(shè)置后,會(huì)導(dǎo)致通過(guò)overridePendingTransition設(shè)置的啟動(dòng)關(guān)閉Activity的動(dòng)畫無(wú)效团滥。需要在style中重新寫如下幾個(gè)動(dòng)畫:
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:windowAnimationStyle">@style/Animation.Activity.Translucent.Style</item>
<item name="android:windowFullscreen">true...
<item name="android:windowIsTranslucent">true...
</style>
<style name="Animation.Activity.Style" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">...
<item name="android:activityOpenExitAnimation">...
<item name="android:activityCloseEnterAnimation">...
<item name="android:activityCloseExitAnimation">...
</style>
<style name="Animation.Activity.Translucent.Style" parent="@android:style/Animation.Translucent">
<item name="android:windowEnterAnimation">...
<item name="android:windowExitAnimation">...
</style>
- Activity之間的跳轉(zhuǎn)可能偶爾會(huì)看到桌面一閃而過(guò)(如果SplashActivity和其他Activity都設(shè)置了透明)竿屹。
小結(jié):一般情況下是只會(huì)給SplashActivity
設(shè)置一個(gè)透明背景的主題,其他Activity
不會(huì)設(shè)置灸姊,經(jīng)過(guò)實(shí)踐拱燃,這種體驗(yàn)是最好的。但是如果要做到App秒開還是不行的力惯,和我們的文章開頭所分析的原理相斥了碗誉。
秒開方案
還是要從 Activity
的 Theme
下手,既然可以讓 Window
白屏(黑屏)或者透明父晶,那么是不是可以設(shè)置其它顏色或者圖片來(lái)實(shí)現(xiàn)App的秒開呢哮缺?答案是肯定的。
實(shí)現(xiàn)原理
我們之前設(shè)置了Window
透明甲喝,實(shí)現(xiàn)了去掉白屏和黑屏尝苇,現(xiàn)在要弄一個(gè)顏色或者圖片來(lái)代替白屏和黑屏,所以首先要把原來(lái)style
中的透明屬性去掉埠胖。然后給Window
設(shè)置一個(gè)背景顏色或者圖片糠溜。
實(shí)現(xiàn)步驟
1、首先在res/drawable下新建一個(gè)layer-list直撤,名字隨意非竿,比如splash.xml:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景顏色 -->
<item android:drawable="@color/white" />
<item>
<!-- 如果是全屏圖時(shí)上面顏色可以不設(shè)置;如果是小圖就必須設(shè)置下顏色 -->
<bitmap
android:gravity="center"
android:src="@drawable/splash_logo_page" />
</item>
</layer-list>
2谋竖、給主題設(shè)置Window背景:
<style name="SplashTheme" parent="AppBaseTheme">
<!-- Splash頁(yè)設(shè)置背景红柱,當(dāng)然頁(yè)可以直接用全屏圖片 -->
<item name="android:windowBackground">@drawable/splash</item>
<item name="android:windowFullscreen">true</item>
<!-- <item name="android:windowIsTranslucent">true</item> --> <!-- 透明背景不要了 -->
</style>
3承匣、在AndroidManifest.xml中定義SplashActivity的theme為SplashTheme:
<activity android:name=".SplashActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
4、SplashActivity的實(shí)現(xiàn)锤悄,在onCreate()啟動(dòng)你的MainActivity即可韧骗,其他什么都別干:
public class SplashActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startActivity(new Intent(this, MainActivity.class));
finish();
}
}
特別注意:
- 為保證啟動(dòng)速度,
SplashActivity
不要調(diào)用setContentView()
方法铁蹈。因?yàn)?code>Activity設(shè)置了layout
宽闲,它在App完全初始化完成后才會(huì)顯示,也會(huì)耗時(shí)握牧。使用該啟動(dòng)畫面實(shí)現(xiàn)也能兼容到上面說(shuō)的白屏和黑屏的問(wèn)題容诬。跟上面的小結(jié)一樣,其他Activity
不要設(shè)置 - 如果Splash有閃屏或者廣告投放之類的業(yè)務(wù)沿腰,可以
setContentView()
并做相關(guān)邏輯览徒,切忌耗時(shí)操作