Skip to content
Elvis Chidera

The (Complete) Android Splash Screen Guide

android, shazam, audio5 min read

In the past, having splash screens in your Android app were not recommended. It didn’t make much sense to intentionally delay the user by adding a splash screen that shows for x seconds. Am sure no one launches an app just to see a splash screen (more on this later).

Getting users to the content they care about should be your #1 priority

Well, when Material Design dropped with a pattern called Launch Screen (Splash Screen), someone from the Android team shared a post on how to do splash screens the ‘right’ way.

Branded Splash Screen

In this post I will walk through 4 common methods of implementing splash screens on the Android platform:

  1. Using a Launcher Theme (The good)

  2. Using a Launcher Theme with a Dedicated Splash Activity (The okay)

  3. Using Timers (The bad)

  4. Using Smart Timers (The ugly)

Using a Launcher Theme

When your app is launched and it isn’t in memory yet, there may be some delay between when the user starts your app and when your launcher Activity’s onCreate() is actually called.

During the ‘cold start’, the window manager tries to draw a placeholder UI using elements from the app theme like the windowBackground. So, rather than showing the default windowBackground (usually white or black), you can change it to a custom drawable that shows your splash screen. This way the splash screen only shows when needed and you don’t slow down your users.

The key is creating a custom theme that overrides android:windowBackground, then replacing that custom theme with your standard theme before calling super.onCreate() in your Activity.

Implementation

In this example, I will assume your app main theme is named AppTheme, but if it’s not then you can replace all occurrence of AppTheme with the name of your app main theme.

You will have to create a new theme for the launcher. The only element we are interested in overriding in this theme is the windowBackground, so the launcher theme will be:

styles.xml
1<?xml version="1.0" encoding="utf-8"?>
2<resources>
3
4 <!-- Your AppTheme or other themes/styles here -->
5
6 <!-- The launcher theme. It sets the main window background to the launch_screen drawable -->
7 <style name=”AppTheme.Launcher”>
8 <item name=”android:windowBackground”>@drawable/launch_screen</item>
9 <!-- Optional, on Android 5+ you can modify the colorPrimaryDark color to match the windowBackground color for further branding-->
10 <!-- <item name="colorPrimaryDark">@android:color/white</item> -->
11 </style>
12
13</resources>

To inherit every other attribute in your main theme, the dot notation was used by prefixing the name of the theme (AppTheme), separated by a period (.).

Let’s define the launch_screen drawable. While you could just use a simple image, it will end up stretched to fill the entire screen. Instead, you can use an XML file such as:

launch_screen.xml
1<?xml version="1.0" encoding="utf-8"?>
2
3<!-- The android:opacity=”opaque” line — this is critical in preventing a flash of black as your theme transitions. -->
4<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
5
6 <!-- The background color, preferably the same as your normal theme -->
7 <item android:drawable="@android:color/white"/>
8
9 <!-- Your product logo - 144dp color version of your app icon -->
10 <item>
11
12 <bitmap
13 android:src="@drawable/product_logo_144dp"
14 android:gravity="center"/>
15
16 </item>
17
18</layer-list>

Then apply the launcher theme to your launcher Activity in your AndroidManifest.xml file using:

AndroidManifest.xml
1android:theme=”@style/AppTheme.Launcher”

The easiest way to transition back to your normal theme in your launcher (main) activity is to call setTheme(R.style.AppTheme) before super.onCreate() and setContentView():

MainActivity.kt
1import android.os.Bundle
2import android.support.v7.app.AppCompatActivity
3
4class MainActivity: AppCompatActivity() {
5
6 override fun onCreate(savedInstanceState: Bundle?) {
7 // Make sure this is before calling super.onCreate
8 setTheme(R.style.AppTheme)
9 super.onCreate(savedInstanceState)
10 // …
11 }
12
13}

You can read more about this approach from the source here.

Pros:

  1. No launch/splash activity needed — there is no delay such as there would be if you were launching a second activity from a dedicated splash screen activity.

  2. No artificial delays — No intentional x seconds delay. Only showing the splash screen when the system is loading up the app.

Cons:

I have seen 3 very common complains about this approach.

  1. The splash screen showing again if the activity was killed and recreated by the system. In most cases, this is not a very serious issue, but you can use Method 2 to fix it.

  2. Some developers want to have a dedicated splash screen Activity that routes to different pages based on some state after the splash screen is done. Again you can easily do this using Method 2 below, but sometimes this dedicated router Activity end up getting really messy.

  3. Not able to load heavy data/components while the splash screen is shown. It’s usually a bad idea to require heavy data or components to be loaded before the app is actually started (there are some exceptions). You can try one of the suggestions below:

i) Try to lazy load your components/modules/libraries. Except a component is really really required for the app to work, try not to load it at launch time rather load it when you need it or use a background thread to load it after the app has started. Try to keep your Application onCreate() as light as possible

ii) Make use of caching. Except that info changes very frequently, you should cache it. So when next the user comes to your app, you can show this cached content while you load more recent content.

I think we should strive to remove things like long splash screens, ProgressDialogs that make the user unable to perform any other action apart from just staring at the screen. You never know how long it will take to load that data from the Internet.

If your app connects to the web, assume that anything that can go wrong will go wrong. This way you can build for the millions of people still using unstable 2g and 3g connections.

Using a Launcher Theme with a Dedicated Splash Activity

This method builds on top Method 1. It requires you to have a dedicated splash screen Activity. This allows you to quickly solve the first 2 issues in Method 1.

All you have to do in this step is to create a new splash Activity and assign the launcher theme to it in your AndroidManifest.xml file (Like in Method 1). Then edit your splash activity to route to the various pages. Look at the example below:

SplashActivity.kt
1import android.os.Bundle
2import android.support.v7.app.AppCompatActivity
3
4class SplashActivity: AppCompatActivity() {
5
6 override fun onCreate(savedInstanceState: Bundle?) {
7 // Make sure this is before calling super.onCreate
8 setTheme(R.style.AppTheme)
9 super.onCreate(savedInstanceState)
10
11 val user = UserDb.getCurrentUser()
12 routeToAppropriatePage(user)
13 finish()
14 }
15
16 private fun routeToAppropriatePage(user: User) {
17 // Example routing
18 when {
19 user == null -> OnboardingActivity.start(this)
20 user.hasPhoneNumber() -> EditProfileActivity.start(this)
21 user.hasSubscriptionExpired() -> PaymentPlansActivity.start(this)
22 else -> HomeActivity.start(this)
23 }
24 }
25
26}

Pros:

  1. Solves the first two issues listed in Method 1.

Cons:

  1. I have seen this routing thing easily get ugly.

  2. Small delay transitioning between Activities.

  3. It’s easy to forget and start doing some long running operations here.

Using Timers

This is the old easy approach. You just have to create a dedicated splash screen Activity that shows up for x seconds, then opens the appropriate activity. You get more flexibility here as you can add animations, custom views or any other element you can normally fit into an Activity layout. A very basic implementation of this will look like this:

SplashActivity.kt
1import android.os.Bundle
2import android.os.Handler
3import android.support.v7.app.AppCompatActivity
4
5class SplashActivity: AppCompatActivity() {
6
7 override fun onCreate(savedInstanceState: Bundle?) {
8 super.onCreate(savedInstanceState)
9 setContentView(R.layout.activity_splash)
10
11 scheduleSplashScreen()
12 }
13
14 private fun scheduleSplashScreen() {
15 val splashScreenDuration = getSplashScreenDuration()
16 Handler().postDelayed(
17 {
18 // After the splash screen duration, route to the right activities
19 val user = UserDb.getCurrentUser()
20 routeToAppropriatePage(user)
21 finish()
22 },
23 splashScreenDuration
24 )
25 }
26
27 private fun getSplashScreenDuration() = 2000L
28
29 private fun routeToAppropriatePage(user: User) {
30 // Example routing
31 when {
32 user == null -> OnboardingActivity.start(this)
33 user.hasPhoneNumber() -> EditProfileActivity.start(this)
34 user.hasSubscriptionExpired() -> PaymentPlansActivity.start(this)
35 else -> HomeActivity.start(this)
36 }
37 }
38
39}

Pros:

  1. You get a chance to show your awesome animation or some custom design you have built. This really makes sense for games or apps directed to kids.

  2. More flexibility on what you can do on the splash screen.

Cons:

  1. Double whammy — Your launcher Activity usually doesn’t show up immediately when the app is launched, especially when your app is cold starting. The user waits for the cold start looking at only the windowBackground and then waits for your splash screen again before getting to your app content.

  2. Your awesome animation or design usually only amaze the user the first few times. After that, most users will find it boring and they want to get to the content. I think Method 4 provides a fix for this.

  3. Most times the extra delay is not worth it.

Using Smart Timers

This builds on Method 3. So rather than make the delay fixed, you vary it based on whether the user is launching the app for the first time or not. Here is an example using SharedPreferences:

SplashActivity.kt
1import android.content.Context
2import android.os.Bundle
3import android.os.Handler
4import android.support.v7.app.AppCompatActivity
5
6class SplashActivity: AppCompatActivity() {
7
8 override fun onCreate(savedInstanceState: Bundle?) {
9 super.onCreate(savedInstanceState)
10 setContentView(R.layout.activity_splash)
11
12 scheduleSplashScreen()
13 }
14
15 private fun scheduleSplashScreen() {
16 val splashScreenDuration = getSplashScreenDuration()
17 Handler().postDelayed(
18 {
19 // After the splash screen duration, route to the right activities
20 val user = UserDb.getCurrentUser()
21 routeToAppropriatePage(user)
22 finish()
23 },
24 splashScreenDuration
25 )
26 }
27
28 private fun getSplashScreenDuration(): Long {
29 val sp = getPreferences(Context.MODE_PRIVATE)
30 val prefKeyFirstLaunch = "pref_first_launch"
31
32 return when(sp.getBoolean(prefKeyFirstLaunch, true)) {
33 true -> {
34 // If this is the first launch, make it slow (> 3 seconds) and set flag to false
35 sp.edit().putBoolean(prefKeyFirstLaunch, false).apply()
36 5000
37 }
38 false -> {
39 // If the user has launched the app, make the splash screen fast (<= 1 seconds)
40 1000
41 }
42 }
43 }
44
45 private fun routeToAppropriatePage(user: User) {
46 // Example routing
47 when {
48 user == null -> OnboardingActivity.start(this)
49 user.hasPhoneNumber() -> EditProfileActivity.start(this)
50 user.hasSubscriptionExpired() -> PaymentPlansActivity.start(this)
51 else -> HomeActivity.start(this)
52 }
53 }
54
55}

Pros:

Inherits all Method 3 pros.

  1. This might fix the issue of the user getting tired of seeing your splash screen showing up for a long period of time.

Cons:

  1. Double whammy — The issue in method 3 still exists here

  2. Most times the extra delay is not worth it.

  3. I haven’t used this method before, but I think there might be some lag while reading from storage.

Okay, so that’s it for splash screens on Android. If I missed any other common implementation, please let me know in the comment section below.

© 2020 by Elvis Chidera. All rights reserved.