简体   繁体   English

Android - 防止启动时出现白屏

[英]Android - Prevent white screen at startup

As we all know, many Android apps display a white screen very briefly before their first Activity comes into focus.众所周知,许多 Android 应用程序在其第一个Activity成为焦点之前会非常短暂地显示白屏。 This problem is observed in the following cases:在以下情况下会出现此问题:

  • Android apps that extend the global Application class and perform major initializations therein. Android 扩展全局Application class 并在其中执行主要初始化的应用程序。 The Application object is always created before the first Activity (a fact that can be observed in the debugger), so this makes sense. Application object 总是在第一个Activity之前创建(可以在调试器中观察到这一事实),因此这是有道理的。 This is the cause of the delay in my case.这就是我的案件延迟的原因。

  • Android apps that display the default preview window before the splash screen. Android 在启动画面之前显示默认预览 window 的应用程序。

Setting android:windowDisablePreview = "true" obviously does not work here.设置android:windowDisablePreview = "true"显然在这里不起作用。 Nor can I set the parent theme of the splash screen to Theme.Holo.NoActionBar as described here , because [unfortunately] my splash screen makes use of an ActionBar .我也不能按照此处所述将启动画面的父主题设置为Theme.Holo.NoActionBar ,因为 [不幸的是] 我的启动画面使用了ActionBar

Meanwhile, apps that do not extend the Application class do not show the white screen at startup.同时,不扩展Application class 的应用在启动时不会显示白屏。

The thing is, ideally the initializations performed in the Application object need to occur before the first Activity is shown.问题是,理想情况下, Application object 中执行的初始化需要在显示第一个Activity之前发生。 So my question is, how can I perform these initializations on app startup without using an Application object?所以我的问题是,如何在使用Application object 的情况下在应用程序启动时执行这些初始化? Possibly using a Thread or Service , I suppose?我想可能使用ThreadService

This is an interesting problem to think about.这是一个值得思考的有趣问题。 I can't bypass it the usual way (by setting the NoActionBar theme), as tragically my Splash screen actually has an ActionBar due to some unrelated reasons.我无法以通常的方式绕过它(通过设置NoActionBar主题),不幸的是,由于一些不相关的原因,我的启动画面实际上有一个ActionBar

Note:笔记:

I have already referred to the following questions:我已经提到了以下问题:

References:参考:

please add this line into your app theme请将这一行添加到您的应用主题中

<item name="android:windowDisablePreview">true</item>

for more information : https://developer.android.com/topic/performance/vitals/launch-time#themed 了解更多信息: https : //developer.android.com/topic/performance/vitals/launch-time#themed

The problem with white background is caused because of android's cold start while the app loads to memory, and it can be avoided with this:白色背景的问题是由于android在应用程序加载到内存时冷启动引起的,可以通过以下方式避免:

public class OnboardingWithCenterAnimationActivity extends AppCompatActivity {
public static final int STARTUP_DELAY = 300;
public static final int ANIM_ITEM_DURATION = 1000;
public static final int ITEM_DELAY = 300;

private boolean animationStarted = false;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    setTheme(R.style.AppTheme);
    getWindow().getDecorView().setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_onboarding_center);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {

    if (!hasFocus || animationStarted) {
        return;
    }

    animate();

    super.onWindowFocusChanged(hasFocus);
}

private void animate() {
    ImageView logoImageView = (ImageView) findViewById(R.id.img_logo);
    ViewGroup container = (ViewGroup) findViewById(R.id.container);

    ViewCompat.animate(logoImageView)
        .translationY(-250)
        .setStartDelay(STARTUP_DELAY)
        .setDuration(ANIM_ITEM_DURATION).setInterpolator(
            new DecelerateInterpolator(1.2f)).start();

    for (int i = 0; i < container.getChildCount(); i++) {
        View v = container.getChildAt(i);
        ViewPropertyAnimatorCompat viewAnimator;

        if (!(v instanceof Button)) {
            viewAnimator = ViewCompat.animate(v)
                    .translationY(50).alpha(1)
                    .setStartDelay((ITEM_DELAY * i) + 500)
                    .setDuration(1000);
        } else {
            viewAnimator = ViewCompat.animate(v)
                    .scaleY(1).scaleX(1)
                    .setStartDelay((ITEM_DELAY * i) + 500)
                    .setDuration(500);
        }

        viewAnimator.setInterpolator(new DecelerateInterpolator()).start();
    }
}
}

layout布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?colorPrimary"
android:orientation="vertical"
>

<LinearLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical"
    android:paddingTop="144dp"
    tools:ignore="HardcodedText"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="16dp"
        android:alpha="0"
        android:text="Hello world"         android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
        android:textColor="@android:color/white"
        android:textSize="22sp"
        tools:alpha="1"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="8dp"
        android:alpha="0"
        android:gravity="center"
        android:text="This a nice text"
      android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse"
        android:textSize="20sp"
        tools:alpha="1"
        />

    <Button
        android:id="@+id/btn_choice1"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:scaleX="0"
        android:scaleY="0"
        android:text="A nice choice"
        android:theme="@style/Button"
        />

    <Button
        android:id="@+id/btn_choice2"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:scaleX="0"
        android:scaleY="0"
        android:text="Far better!"
        android:theme="@style/Button"
        />

</LinearLayout>

<ImageView
    android:id="@+id/img_logo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:src="@drawable/img_face"
    tools:visibility="gone"
    />
</FrameLayout>

img face头像

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:opacity="opaque">

<item android:drawable="?colorPrimary"/>
<item>
    <bitmap
        android:gravity="center"
        android:src="@drawable/img_face"/>
</item>

Add this theme to your splashscreen in the manifest将此主题添加到清单中的启动画面

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowBackground">@null</item>
</style>

<style name="AppTheme.CenterAnimation">
    <item name="android:windowBackground">@drawable/ll_face_logo</item>
</style>

which will produce efect like this这将产生这样的效果

一只忙碌的猫

for more details and more solutions you can check this BlogPost有关更多详细信息和更多解决方案,您可以查看此博客帖子

Please copy and paste these two lines in your manifest app theme ie res/styles/AppTheme.请将这两行复制并粘贴到您的清单应用主题中,即 res/styles/AppTheme。 then it will work like charm..然后它会像魅力一样工作..

<item name="android:windowDisablePreview">true</item>
<item name="android:windowIsTranslucent">true</item>

Recommended way of solving this problem is missing in the answers.答案中缺少解决此问题的推荐方法。 So I am adding my answer here.所以我在这里添加我的答案。 The white-screen-at-startup problem occurs because of the initial blank screen that the system process draws when launching the app.启动时出现白屏问题是由于系统进程在启动应用程序时绘制的初始空白屏幕。 A common way to solve this is by turning off this initial screen by adding this to your styles.xml file.解决此问题的常用方法是通过将其添加到styles.xml文件来关闭此初始屏幕。

<item name="android:windowDisablePreview">true</item>

But according to android documentation this can result in longer startup time.但根据 android 文档,这可能会导致更长的启动时间。 Recommended way of avoiding this initial white screen according to google is to use activity's windowBackground theme attribute and provide a simple custom drawable for the starting activity.根据谷歌推荐的避免这种初始白屏的方法是使用活动的windowBackground主题属性并为启动活动提供一个简单的自定义可绘制对象。

Like this:像这样:

Drawable Layout file, my_drawable.xml可绘制布局文件, my_drawable.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
  <!-- The background color, preferably the same as your normal theme -->
  <item android:drawable="@android:color/white"/>
  <!-- Your product logo - 144dp color version of your app icon -->
  <item>
    <bitmap
      android:src="@drawable/product_logo_144dp"
      android:gravity="center"/>
  </item>
</layer-list>

Create a new style in your styles.xml在你的styles.xml创建一个新样式

<!-- Base application theme. -->
<style name="AppTheme">
    <!-- Customize your theme here. -->               
</style>

<!-- Starting activity theme -->
<style name="AppTheme.Launcher">
    <item name="android:windowBackground">@drawable/my_drawable</item>
</style>

Add this theme to your starting activity in the Manifest file将此主题添加到清单文件中的起始活动

<activity ...
android:theme="@style/AppTheme.Launcher" />

And when you want to transition back to your normal theme call setTheme(R.style.Apptheme) before calling super.onCreate() and setContentView()当您想在调用super.onCreate()setContentView()之前转换回正常主题时调用setTheme(R.style.Apptheme) setContentView()

public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    // Make sure this is before calling super.onCreate
    setTheme(R.style.Theme_MyApp);
    super.onCreate(savedInstanceState);
    // ...
  }
}

This is the recommended way to solve the problem and this is from google Material Design patterns.这是解决问题的推荐方法,来自 google Material Design patterns。

First of all, to remove the white screen read this -https://www.bignerdranch.com/blog/splash-screens-the-right-way/首先,要删除白屏,请阅读此内容 -https://www.bignerdranch.com/blog/splash-screens-the-right-way/

But more importantly, optimize your initial load and defer any heavy work to when you have time to run it.但更重要的是,优化您的初始负载并将任何繁重的工作推迟到您有时间运行它时。 Post your application class here if you want us to take a look at it.如果您希望我们查看它,请在此处发布您的应用程序课程。

Have you tried setting the android:windowBackground attribute in the theme of your launcher activity, to either a color or a drawable?您是否尝试将启动器活动主题中的android:windowBackground属性设置为颜色或可绘制对象?

For example this:例如这个:

<item name="android:windowBackground">@android:color/black</item>

when added to the Launcher activity theme will show a black color (rather than the white color) on startup.当添加到 Launcher 活动主题时,将在启动时显示黑色(而不是白色)。 This is an easy trick to hide long initialisation, while showing your users something, and it works fine even if you subclass the Application object.这是隐藏长初始化的一个简单技巧,同时向您的用户展示一些东西,即使您对 Application 对象进行子类化,它也能正常工作

Avoid using other constructs (even Threads) for doing long initialisation tasks, because you may end up not being able to control the lifecycle of such constructs.避免使用其他构造(甚至线程)来执行长时间的初始化任务,因为您最终可能无法控制此类构造的生命周期。 The Application object is the correct place for doing exactly this type of actions. Application 对象是执行此类操作的正确位置。

I added the following two lines in my theme under styles.xml我在styles.xml下的主题中添加了以下两行

    <item name="android:windowDisablePreview">true</item>
    <item name="android:windowBackground">@null</item>

Worked like a charm像魅力一样工作

I had same issue, you have to update your style.我有同样的问题,你必须更新你的风格。

style.xml样式文件

<!-- Base application theme. -->
 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

        <!-- Customize your theme here. -->
        <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowDisablePreview">true</item>
        <item name="android:windowBackground">@null</item>
        <item name="android:windowIsTranslucent">true</item>

 </style>

Your manifest file should looks like below.您的清单文件应如下所示。

<application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
     // Other stuff
</application>

Outout:输出:

在此处输入图片说明

Hope this would help you.希望这会帮助你。

Within the lifecycle callback methods, you can declare how your activity behaves when the user leaves and re-enters the activity.在生命周期回调方法中,您可以声明您的 Activity 在用户离开和重新进入 Activity 时的行为方式。 Remember that the way Android is designed, there is a lifecycle for each and every app.请记住,Android 的设计方式是,每个应用程序都有一个生命周期。 If you put too much load to the onCreate() method (which is the method used to load the layout files and initalise any controls you have in it), then the white screen will become more visible, as the layout file will take longer to load.如果对onCreate()方法(该方法用于加载布局文件并初始化其中的任何控件onCreate()施加过多负载,则白屏将变得更加明显,因为布局文件需要更长的时间负载。

I suggest using several different methods when starting an activity.我建议在开始活动时使用几种不同的方法。 Such are the onStart() (being called as the first thing once the app is loaded), onActivityCreated() (being called after the layout is displayed and useful if you are making any data processing upon starting the activity).例如onStart() (在应用程序加载后首先调用), onActivityCreated() (在布局显示后调用,如果您在启动 Activity 时进行任何数据处理,则很有用)。

To make it easier for you, below is the official activity lifecycle diagram:为了方便您,下面是官方的活动生命周期图:

在此处输入图片说明

Please try this once.请尝试一次。

  1. Create a drawable file splash_background.xml创建可绘制文件 splash_background.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@color/{your color}" />

    <item>
        <bitmap
            android:layout_width="@dimen/size_250"
            android:layout_height="@dimen/size_100"
            android:gravity="center"
            android:scaleType="fitXY"
            android:src="{your image}"
            android:tint="@color/colorPrimary" />
    </item>

</layer-list>
  1. Put this in styles.xml把它放在styles.xml中

     <style name="SplashTheme" parent="Theme.AppCompat.NoActionBar"> <item name="android:windowBackground">@drawable/splash_background</item> </style>
  2. In your AndroidMainfest.xml set the above theme to Launch activity.在您的 AndroidMainfest.xml 中,将上述主题设置为 Launch 活动。

     <activity android:name=".SplashScreenActivity" android:screenOrientation="portrait" android:theme="@style/SplashTheme" android:windowSoftInputMode="stateVisible|adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>

Did you try to put initialization to onActivityCreated ?您是否尝试将初始化置于onActivityCreated

Inside Application class :内部Application类:

 registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                if(activity.getClass().equals(FirstActivity.class) {
                    // try without runOnUiThread if it will not help
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            new InitializatioTask().execute();
                        }
                    });
                }
            }

            @Override
            public void onActivityStarted(Activity activity) {

            }

            @Override
            public void onActivityResumed(Activity activity) {

            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });

As you are already aware why this white screen is there, as due to background processes or application initialization or large files, so just check below idea for overcome from this.由于您已经知道为什么会出现这个白屏,因为后台进程或应用程序初始化或大文件,因此请查看以下想法以克服此问题。

To prevent this white screen on beginning of the app, one way is splash screen, this is just a way not final and you must have to use.为了防止应用程序开始时出现此白屏,一种方法是启动画面,这只是一种非最终方法,您必须使用。

When you will show splash screen from your splash.xml file, then also this issue will be remain same,当您从 splash.xml 文件显示启动画面时,此问题也将保持不变,

So you have to create ont style in style.xml file for splash screen and there you have to set window background as your splash image and then apply that theme to your splash activity from manifest file.因此,您必须在 style.xml 文件中为启动画面创建 ont 样式,并且必须将窗口背景设置为启动图像,然后将该主题应用于清单文件中的启动活动。 So now when you will run app, first it will set theme and by this way user will be able to see directly splash image instead of white screen.所以现在当你运行应用程序时,首先它会设置主题,这样用户将能够直接看到启动图像而不是白屏。

Both properties works两个属性都有效

    <style name="AppBaseThemeDark" parent="@style/Theme.AppCompat">
            <!--your other properties -->
            <!--<item name="android:windowDisablePreview">true</item>-->
            <item name="android:windowBackground">@null</item>
            <!--your other properties -->
    </style>

For anyone having that white screen while debbuging, be aware that if you are debbuging it will take longer to load.对于调试时出现白屏的任何人,请注意,如果您正在调试,加载时间将更长。 If you build your the release APK and install it on your phone, you will notice that it takes a lot less to load.如果您构建发布版 APK 并将其安装在手机上,您会注意到加载所需的时间要少得多。

So startup time with debbug version is not equal to startup time with release version.所以 debbug 版本的启动时间不等于 release 版本的启动时间。

According to Google's recommendation Here , you should not prevent this white screen from launching.根据Google 的建议Here ,您不应阻止此白屏启动。 You can use this theme attribute to turn off the initial blank screen that the system process draws when launching the app.您可以使用此主题属性关闭系统进程在启动应用程序时绘制的初始空白屏幕。

<item name="android:windowDisablePreview">true</item>

However, This approach is not recommended because it can result in a longer startup time than apps that don't suppress the preview window.但是,不建议使用此方法,因为与不抑制预览窗口的应用程序相比,它可能会导致更长的启动时间。 Also, it forces the user to wait with no feedback while the activity launches, making them wonder if the app is functioning properly.此外,它会强制用户在 Activity 启动时在没有反馈的情况下等待,让他们怀疑应用程序是否正常运行。

They recommend to use the activity's windowBackground theme attribute to provide a simple custom drawable for the starting activity instead of disabling the preview window.他们建议使用活动的 windowBackground 主题属性为起始活动提供简单的自定义可绘制对象,而不是禁用预览窗口。

Therefore, here is the recommended solution:因此,这里是推荐的解决方案:

First, create a new drawable file for example startup_screen.xml首先,创建一个新的可绘制文件,例如 startup_screen.xml

 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
    <!-- The background color, preferably the same as normal theme -->
    <item android:drawable="@android:color/white"/>
    <!-- Product logo - 144dp color version of App icon -->
    <item>
        <bitmap
            android:src="@drawable/logo"
            android:gravity="center"/>
    </item>
 </layer-list>

Second, reference it from your style file.其次,从您的样式文件中引用它。 If you use Night mode.如果您使用夜间模式。 Add it in both themes.xml files.将它添加到两个 themes.xml 文件中。

<!-- Start Up Screen -->
<style name="AppThemeLauncher" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
     <item name="android:statusBarColor" tools:targetApi="l">@color/lightGray</item>
     <item name="android:windowBackground">@drawable/startup_screen</item>
</style>

If you notice, I added statusBarColor attribute to change the color of status Bar according to my custom design.如果您注意到,我添加了 statusBarColor 属性以根据我的自定义设计更改状态栏的颜色。

Then, Add AppThemeLauncher Theme in your current activity.然后,在您当前的活动中添加AppThemeLauncher主题。

<activity
    android:name=".MainActivity"
    android:theme="@style/AppThemeLauncher"/>

If you want to transition back to your normal theme, call setTheme(R.style.AppTheme) before calling super.onCreate() and setContentView():如果您想转换回正常主题,请在调用 super.onCreate() 和 setContentView() 之前调用 setTheme(R.style.AppTheme):

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Make sure this is before calling super.onCreate
        setTheme(R.style.AppTheme)
        super.onCreate(savedInstanceState)
        // ...
    }
}

Just write the item in values/styles.xml:只需在 values/styles.xml 中写入项目:

<item name="android:windowBackground">@android:color/black</item>

For example, in the AppTheme:例如,在 AppTheme 中:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowNoTitle">true</item>
    <item name="windowActionBar">false</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowContentOverlay">@null</item>

    <item name="android:windowBackground">@android:color/black</item>

    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>
Style :- 
<style name="SplashViewTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/splash</item>
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

In Manifest :- 
<activity android:name=".SplashActivity"
        android:theme="@style/SplashViewTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

Delete删除

<style name="AppTheme.Launcher">
    <item name="android:windowBackground">@drawable/splashscreen</item>
</style>

from style.xml file来自 style.xml 文件

You should have colors.xml on values-night (create alongside values folder if it doesn't already exist) folder for dark theme colors.您应该在 values-night 上拥有colors.xml (如果它尚不存在,则在 values 文件夹旁边创建)用于深色主题 colors 的文件夹。 eg.例如。

<resources>
    <color name="status_bar">#0e0e0e</color>
</resources>

( colors.xml on regular values folder will be used for light theme) (常规值文件夹上的colors.xml将用于浅色主题)

And on styles.xml which supplies your app theme you will have entry for background and statusbar which takes necessary values.在提供您的应用程序主题的styles.xml上,您将拥有背景和状态栏条目,它们需要必要的值。 eg.例如。

<style name="Theme.<AppName>" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/red700</item>
        <item name="colorPrimaryDark">@color/red900</item>
        <item name="colorAccent">@color/red700</item>
        <item name="android:statusBarColor">@color/status_bar</item>
        <item name="android:background">@color/status_bar</item>
    </style>

This style is referenced on AndroidManifest.xml file此样式在 AndroidManifest.xml 文件中引用

android:theme="@style/Theme.<AppName>">

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM