Splash screens

Android 12 adds the SplashScreen API, which enables a new app launch animation for all apps when running on a device with Android 12 or higher. This includes an into-app motion at launch, a splash screen showing your app icon, and a transition to your app itself.

Example of a splash screen
Figure 1: Example of a splash screen

The new experience brings standard design elements to every app launch, but it’s also customizable so your app can maintain its unique branding.

In addition to using the SplashScreen API directly, you can also use the SplashScreen compat library, which wraps the SplashScreen API.

How the splash screen works

When a user launches an app while the app's process is not running (a cold start) or the Activity has not been created (a warm start), the following events occur. (The splash screen is never shown during a hot start.)

  1. The system shows the splash screen using themes and any animations that you've defined.

  2. When the app is ready, the splash screen is dismissed and the app is displayed.

Elements and mechanics of the animation

The elements of the animation are defined by XML resource files in the Android Manifest. There are light and dark mode versions for each.

They consist of the window background, animated app icon, and the icon background:

Elements of a splash screen
Figure 2: Customizable elements of a splash screen

Be aware of the following considerations regarding these elements:

  • The app icon (1) should be a vector drawable, and it can be static or animated. Although animations can have an unlimited duration, we recommend that it not exceed 1,000 milliseconds. By default, the launcher icon is used.

  • The icon background (2) is optional, and is useful if more contrast is needed between the icon and the window background. If you use an adaptive icon, its background is displayed if there is enough contrast with the window background.

  • As with adaptive icons, one-third of the foreground is masked (3).

  • The window background (4) consists of a single opaque color. If the window background is set and is a plain color, it is used by default if the attribute is not set.

The splash screen animation mechanics consist of enter and exit animations.

  • The enter animation consists of the system view to the splash screen. This is controlled by the system and is not customizable.

  • The exit animation consists of the animation run that hides the splash screen. If you want to customize it, you'll have access to the SplashScreenView and its icon and can run any animation on them, with settings for transform, opacity, and color. In that case, the splash screen needs to be manually removed when the animation is done.

Customize the splash screen in your app

By default, SplashScreen uses the windowBackground of your theme if it's a single color and the launcher icon. The customization of the splash screen is done by adding attributes to the app theme.

Your app's splash screen can be customized in any of the following ways:

  • Setting theme attributes to change its appearance

  • Keeping it on-screen for a longer period

  • Customizing the animation for dismissing the splash screen

Set a theme for the splash screen to change its appearance

You can specify the following attributes in your Activity theme to customize the splash screen for your app. If you already have a legacy splash screen implementation that uses attributes like android:windowBackground, consider providing an alternate resource file for Android 12 and higher.

  1. Use windowSplashScreenBackground to fill the background with a specific single color:

    <item name="android:windowSplashScreenBackground">@color/...</item>
    
  2. Use windowSplashScreenAnimatedIcon to replace an icon in the center of the starting window. If the object is animatable and drawable through AnimationDrawable and AnimatedVectorDrawable, you also need to set windowSplashScreenAnimationDuration to play the animation while showing the starting window.

    <item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
    
  3. Use windowSplashScreenAnimationDuration to indicate the duration of the splash screen icon animation. Setting this won't have any effect on the actual time during which the splash screen is shown, but you can retrieve it when customizing the splash screen exit animation using SplashScreenView#getIconAnimationDuration. See Keep the splash screen for longer periods in the following section for further details.

    <item name="android:windowSplashScreenAnimationDuration">1000</item>
    
  4. Use windowSplashScreenIconBackgroundColor to set a background behind the splash screen icon. This is useful if there isn’t enough contrast between the window background and the icon.

    <item name="android:windowSplashScreenIconBackgroundColor">@color/...</item>
    
  5. Optionally, you can use windowSplashScreenBrandingImage to set an image to be shown at the bottom of the splash screen. The design guidelines recommend against using a branding image.

    <item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
    

Keep the splash screen on-screen for longer periods

The splash screen is dismissed as soon as your app draws its first frame. If you need to load a small amount of data such as in-app settings from a local disk asynchronously, you can use ViewTreeObserver.OnPreDrawListener to suspend the app to draw its first frame.

If your starting activity finishes before drawing (for example, by not setting the content view and finishing before onResume), the pre-draw listener is not needed.

Kotlin

// Create a new event for the activity.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Set the layout for the content view.
    setContentView(R.layout.main_activity)

    // Set up an OnPreDrawListener to the root view.
    val content: View = findViewById(android.R.id.content)
    content.viewTreeObserver.addOnPreDrawListener(
        object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                // Check if the initial data is ready.
                return if (viewModel.isReady) {
                    // The content is ready; start drawing.
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                } else {
                    // The content is not ready; suspend.
                    false
                }
            }
        }
    )
}

Java

// Create a new event for the activity.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set the layout for the content view.
    setContentView(R.layout.main_activity);

    // Set up an OnPreDrawListener to the root view.
    final View content = findViewById(android.R.id.content);
    content.getViewTreeObserver().addOnPreDrawListener(
            new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    // Check if the initial data is ready.
                    if (mViewModel.isReady()) {
                        // The content is ready; start drawing.
                        content.getViewTreeObserver().removeOnPreDrawListener(this);
                        return true;
                    } else {
                        // The content is not ready; suspend.
                        return false;
                    }
                }
            });
}

Customize the animation for dismissing the splash screen

You can further customize the animation of the splash screen through Activity.getSplashScreen().

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...

    // Add a callback that's called when the splash screen is animating to
    // the app content.
    splashScreen.setOnExitAnimationListener { splashScreenView ->
        // Create your custom animation.
        val slideUp = ObjectAnimator.ofFloat(
            splashScreenView,
            View.TRANSLATION_Y,
            0f,
            -splashScreenView.height.toFloat()
        )
        slideUp.interpolator = AnticipateInterpolator()
        slideUp.duration = 200L

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.doOnEnd { splashScreenView.remove() }

        // Run your animation.
        slideUp.start()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...

    // Add a callback that's called when the splash screen is animating to
    // the app content.
    getSplashScreen().setOnExitAnimationListener(splashScreenView -> {
        final ObjectAnimator slideUp = ObjectAnimator.ofFloat(
                splashScreenView,
                View.TRANSLATION_Y,
                0f,
                -splashScreenView.getHeight()
        );
        slideUp.setInterpolator(new AnticipateInterpolator());
        slideUp.setDuration(200L);

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                splashScreenView.remove();
            }
        });

        // Run your animation.
        slideUp.start();
    });
}

By the start of this callback, the animated vector drawable on the splash screen has begun. Depending on the duration of the app launch, the drawable might be in the middle of its animation. Use SplashScreenView.getIconAnimationStart to know when the animation started. You can calculate the remaining duration of the icon animation as follows:

Kotlin

// Get the duration of the animated vector drawable.
val animationDuration = splashScreenView.iconAnimationDuration
// Get the start time of the animation.
val animationStart = splashScreenView.iconAnimationStart
// Calculate the remaining duration of the animation.
val remainingDuration = if (animationDuration != null && animationStart != null) {
    (animationDuration - Duration.between(animationStart, Instant.now()))
        .toMillis()
        .coerceAtLeast(0L)
} else {
    0L
}

Java

// Get the duration of the animated vector drawable.
Duration animationDuration = splashScreenView.getIconAnimationDuration();
// Get the start time of the animation.
Instant animationStart = splashScreenView.getIconAnimationStart();
// Calculate the remaining duration of the animation.
long remainingDuration;
if (animationDuration != null && animationStart != null) {
    remainingDuration = animationDuration.minus(
            Duration.between(animationStart, Instant.now())
    ).toMillis();
    remainingDuration = Math.max(remainingDuration, 0L);
} else {
    remainingDuration = 0L;
}