三十四、【Android Splash閃屏頁秒開 Activity白屏、Activity黑屏問題 】

只非魚發表於2017-12-13

今天在測試應用的時候發現,在進入閃屏頁之前,會有1-2秒的白屏或者黑屏(顏色與設定的主題有關)。

一、本文章主要解決:
  1. APP啟動時白屏/黑屏、Activity開啟時白屏/黑屏。
  2. APP啟動速度慢,如何實現點選ICON後APP秒開。APP啟動加速。
二、瞭解繪製整個視窗:
  1. 繪製背景。
  2. 繪製View本身的內容。
  3. 繪製子View。
  4. 繪製修飾內容(例如滾動條)。

閃屏原因剖析StartingWindow(Preview Window)

三、常見的做法:

我們正常開發中會在Activity的onCreate()方法中呼叫setContentView(View)設定該Activity的顯示佈局。

當開啟一個Activity時,如果這個Activity所屬Application還沒有在執行,系統會為這個Activity的建立一個程式(每開啟一個程式都會有一個Application,所以Application的onCreate()可能會被呼叫多次),但程式的建立與初始化都需要時間,在這個動作完成之前,如果初始化的時間過長,螢幕上可能沒有任何動靜,使用者會以為沒有點到按鈕。所以既不能停在原來的地方又沒到顯示新的介面,怎麼辦呢?這就有了StartingWindow(也稱之為PreviewWindow)的出現,這樣看起來就像Activity已經啟動起來了,只是資料內容還沒有初始化好。

StartingWindow一般出現在應用程式程式建立並初始化成功前,所以它是個臨時視窗,對應的WindowTypeTYPE_APPLICATION_STARTING。目的是告訴使用者,系統已經接受到操作,正在響應,在程式初始化完成後實現目的UI,同時移除這個視窗。

StartingWindow就是我們要討論的白屏和黑屏的“元凶”,一般情況下我們會對ApplicationActivity設定Theme,系統會根據設定的Theme初始化StartingWindowWindow佈局的頂層是DecorViewStartingWindow顯示一個空DecorView,但是會給這個DecorView應用這個Activity指定的Theme,如果這個Activity沒有指定Theme就用Application的(Application系統要求必須設定Theme)。

四、解決辦法

1.閃屏頁Activity設定透明的主題

<style name="SplashTheme" parent="AppTheme">
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowIsTranslucent">true</item>
</style>
複製程式碼

如上設定後APPActivity啟動時,我們的StartingWindow會應用我們這個透明背景的主題,跳轉時確實沒有白屏和黑屏了,但是這樣設定會產生如下後果: 1.1、給SplashActivity設定後,使用者點選我們APP圖示後,需要等待2秒左右的時候才會顯示contentView。造成了APP啟動速度慢的假象,其實Activity已經啟動了,只是background是透明的,這時候你點選桌面的其他地方是無效的。這樣就和Google的初衷背道而馳了,所以還要繼續往下看。 1.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>
複製程式碼

1.3、Activity之間的跳轉可能偶爾會看到桌面一閃而過(如果SplashActivity和其他Activity都設定了透明)。

小結:一般情況下是隻會給SplashActivity設定一個透明背景的主題,其他Activity不會設定,經過實踐,這種體驗是最好的。但是如果要做到APP秒開還是不行的,和我們的文章開頭所分析的原理相斥了。

2.實現秒開的效果 我們之前設定了Window透明,實現了去掉白屏和黑屏,現在要弄一個顏色或者圖片來代替白屏和黑屏,所以首先要把原來style 中的透明屬性去掉。然後給Window設定一個背景顏色或者圖片。

實現步驟 2.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.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作為背景,當然也可以設定個全屏的圖片。

2.3、在AndroidManifest.xml中定義SplashActivitythemeSplashTheme

<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>
複製程式碼

2.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();
    }
}
複製程式碼

相關文章