帶你重新認識:Android Splash頁秒開 Activity白屏 Activity黑屏
版權聲明:轉載必須註明本文轉自嚴振杰的博客: http://blog.csdn.net/yanzhenjie1003
本篇博客要剖析和解決的兩個問題:
1. APP啓動時白屏/黑屏、Activity打開時白屏/黑屏。
2. APP啓動速度慢,如何實現點擊ICON後APP秒開。APP啓動加速。
APP啓動時白屏/黑屏、Activity打開時白屏/黑屏
首先要說明的是無論是APP啓動,還是startActivity
都是Activity的啓動,所以這歸根結底是一個問題,看完本博客就明白了。
這是一個很多新手或者從事Android開發已經一年多的同學們可能遇到的疑問,究其原因是對Activity的啓動機制和Activity的繪製機智不太瞭解。
繪製整個窗口需要按順序執行以下幾個步驟:
1. 繪製背景。
2. 繪製View本身的內容。
3. 繪製子View。
4. 繪製修飾內容(例如滾動條)。
這裏是主要的四步,還有些其他對於今天內容不太重要省去沒寫。
閃屏原因剖析StartingWindow(Preview Window)
我們正常開發中會在Activity
的onCreate()
方法中調用setContentView(View)
設置該Activity
的顯示佈局,那麼問題就來了,既然我們設置了佈局,爲什麼啓動的時候還會白屏或者黑屏而不是顯示我set
的佈局呢?下面就帶領大家一起來剖析一下原因。
當打開一個Activity
時,如果這個Activity
所屬Application
還沒有在運行,系統會爲這個Activity
的創建一個進程(每開啓一個進程都會有一個Application
,所以Application
的onCreate()
可能會被調用多次),但進程的創建與初始化都需要時間,在這個動作完成之前,如果初始化的時間過長,屏幕上可能沒有任何動靜,用戶會以爲沒有點到按鈕。所以既不能停在原來的地方又沒到顯示新的界面,怎麼辦呢?這就有了StartingWindow
(也稱之爲PreviewWindow
)的出現,這樣看起來就像Activity
已經啓動起來了,只是數據內容還沒有初始化好。
StartingWindow
一般出現在應用程序進程創建並初始化成功前,所以它是個臨時窗口,對應的WindowType
是TYPE_APPLICATION_STARTING
。目的是告訴用戶,系統已經接受到操作,正在響應,在程序初始化完成後實現目的UI,同時移除這個窗口。
這個StartingWindow
就是我們要討論的白屏和黑屏的“元兇”,一般情況下我們會對Application
和Activity
設置Theme
,系統會根據設置的Theme
初始化StartingWindow
。Window
佈局的頂層是DecorView
,StartingWindow
顯示一個空DecorView
,但是會給這個DecorView
應用這個Activity
指定的Theme
,如果這個Activity
沒有指定Theme
就用Application
的(Application
系統要求必須設置Theme
)。
在Theme
中可以指定窗口的背景,Activity
的ICON
,APP整體文字顏色等,如果說沒有指定任何屬性,就會用默認的屬性,也就是上文中提到的空DecorView
,所以我們的白屏和黑屏和空DecorView
息息相關,我們給APP設置的Style就決定了是白屏還是黑屏。
1、如果選擇了Black
的系列的主題那麼Activity
跳轉的時候就是黑屏:
@android:style/Theme.Black"
2、如果選擇了Light
的系列的主題那麼Activity
跳轉的時候就是白屏:
@android:style/Theme.Light"
解決辦法
通常的解決辦法都是給Activity
設置一個透明背景的主題:
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">true</item>
</style>
如上設置後APP和Activity
啓動時,我們的StartingWindow
會應用我們這個透明背景的主題,跳轉時確實沒有白屏和黑屏了,但是這樣設置會產生如下後果:
1、給SplashActivity
設置後,用戶點擊我們APP圖標後,需要等待2秒左右的時候纔會顯示contentView。造成了APP啓動速度慢的假象,其實Activity
已經啓動了,只是background
是透明的,這時候你點擊桌面的其他地方是無效的。這樣就和Google的初衷背道而馳了,所以還要繼續往下看。
2、給其他Activity設置後,會導致通過overridePendingTransition
設置的啓動關閉Activity的動畫無效。需要在style中重新寫如下幾個動畫:
<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>
3、Activity之間的跳轉可能偶爾會看到桌面一閃而過(如果SplashActivity和其他Activity都設置了透明)。
小結:一般情況下是隻會給SplashActivity
設置一個透明背景的主題,其他Activity
不會設置,經過實踐,這種體驗是最好的。但是如果要做到APP秒開還是不行的,和我們的文章開頭所分析的原理相斥了。
秒開方案
那像媽媽去哪兒、美團、淘寶等APP是如何實現秒開的?其實看完上面的原理分析,這個基本上也就明白了。
還是從Activity
的Theme
下手,既然可以讓Window
白屏黑屏或者透明,那麼是不是可以設置其他顏色或者圖片來實現APP的秒開呢?答案是肯定的。
原理
我們之前設置了Window
透明,實現了去掉白屏和黑屏,現在要弄一個顏色或者圖片來代替白屏和黑屏,所以首先要把原來style
中的透明屬性去掉。然後給Window
設置一個背景顏色或者圖片。
實現步驟
1、首先在res/drawable下新建一個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>
<!-- 圖片 -->
<bitmap
android:gravity="center"
android:src="@drawable/wel_page" />
</item>
</layer-list>
layer-list
大家都會寫吧,上面是背景顏色,下面是一張圖,這張圖可以是全屏的圖,可以是一張小圖。如果是全屏的圖,那上面的顏色也可以不用設置,如果是小圖,就要指定下顏色了,並且可以指定圖片在位置。
2、給主題設置Window
背景:
<style name="SplashTheme" parent="AppBaseTheme">
<!-- 歡迎頁背景引用剛纔寫好的 -->
<item name="android:windowBackground">@drawable/splash</item>
<item name="android:windowFullscreen">true</item>
<!-- <item name="android:windowIsTranslucent">true</item> --> <!-- 透明背景不要了 -->
</style>
上面的<item name="android:windowBackground">
可以用我們上面的layer-list
作爲背景,當然也可以設置個全屏的圖片。
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的實現,在onCreate()
啓動你的MainActivity
即可,其他什麼都別幹:
public class SplashActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startActivity(new Intent(this, MainActivity.class));
finish();
}
}
特別注意:爲保證啓動速度,SplashActivity
不要調用setContentView()
方法。因爲Activity
設置了layout
,它在App
完全初始化完成後纔會顯示,也會耗時。使用該啓動畫面實現也能兼容到上面說的白屏和黑屏的問題。跟上面的小結一樣,其他Activity
不要設置。
特別更新:博客剛發不久,有人跟我吐槽說,SplashActivity
中需要做一個初始化的操作,被我放哪裏了?可能是因爲在上面第四點中說了個直接啓動MainActivity
其他什麼都不別幹,這裏可以把MainActivity
換成別的InitializeActivity
,初始化、引導頁的判斷可以放在這裏,這裏都操作完了再啓動MainActivity
、CoreActivity
等即可。
當然大多數必要的初始化可以放在Application
中(建議再啓動一個子線程),因爲你的進程說不定什麼時候就被系統回收了,這時候直接啓動時是啓動被系統回收的時候正處於Resume
狀態的那個Activity
,那你的初始化的`Activity
就不會被執行了。
參考:
http://cyrilmottier.com/2013/01/23/android-app-launching-made-gorgeous/
https://www.bignerdranch.com/blog/splash-screens-the-right-way/
版權聲明:轉載必須註明本文轉自嚴振杰的博客: http://blog.csdn.net/yanzhenjie1003