Android 12 jetpack splashScreen API summary

Splash Screen document address of official Android 12
The official Splash Screen compatible library supports all versions of the system

This article focuses on the following three questions:

  1. What can we learn from the Android 12 SplashScreen API?
  2. What is the new SplashScreen compatible library? What can it look like?
  3. Classmate a: I want to see the source code of Android 12 splashScreen, OK?

SplashScreen usage

First, we need to upgrade compileSdk and targetsdk (optional) to 31.

Android 12 version

(A) . theme and appearance configuration

<!--At the end of the article, we will provide the link address containing all the examples. If necessary, please turn to the end of the article-->

<!-- values-v31/themes.xml -->
<!--Single color fill「Startup screen」Window background-->
<item name="android:windowSplashScreenBackground">@color/...</item>

<!--「Startup screen」Center icon,
     Can configure AnimationDrawable and AnimatedVectorDrawable Type drawable-->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>

<!--「Startup screen」The duration of the center icon animation. This property has no effect on the actual time displayed on the screen-->
<item name="android:windowSplashScreenAnimationDuration">1000</item>

<!--「Startup screen」Set background behind Center Icon-->
<item name="android:windowSplashScreenIconBackgroundColor">@color/...</item>

<!--「Startup screen」Brand icon shown at the bottom-->
<item name="android:windowSplashScreenBrandingImage">@drawable/...</item>

(B) . extended start screen

val content: View = findViewById(android.R.id.content)
    content.viewTreeObserver.addOnPreDrawListener(
        object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                // Simulate the initialization of some data, and then cancel the suspension
                return if (viewModel.isReady) {
                    // Cancel the hold and resume page content drawing
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                } else {
                    // Hang, the content is not ready
                    false
                }
            }
        }
    )

(C) close the animation of the start screen

// Customize your own closed animation
splashScreen.setOnExitAnimationListener { splashScreenView ->
        val slideUp = ObjectAnimator.ofFloat(
            // You can control and write any animation by yourself. Here we test to make the icon move
            splashScreenView.iconView,
            View.TRANSLATION_Y,
            0f,
            -splashScreenView.height.toFloat()
        )
        slideUp.interpolator = AnticipateInterpolator()
        slideUp.duration = 200L
        slideUp.doOnEnd { splashScreenView.remove() }
        slideUp.start()
    }

(D) . problems encountered

  • Android: what are the image size requirements defined by windows splash screen branding image? Always feel a little stretched;
  • Using AnimationDrawable or AnimatedVectorDrawable to set the center icon, the "center icon" will disappear, and the static icon will not have this problem;
  • Android 12 parent theme setting android:windowBackground is overwritten, and no effect can be seen

Question 1: I don't see the specific value or scale in the source code. What should I do?

Tip: use an oversized square icon to test it. Stretching doesn't matter. We want the proportion, and then measured the proportion as 2.5:1. Therefore, when designing the brand name icon, we can set it as an icon with a ratio of 2.5:1 such as 400 * 160

Question 2: test the problem that the center icon will flash and disappear,
Test 1: static Icon, test 2: dynamic Icon

Static Center Icon - normal... Floating

Let's test the dynamic center icon. In order to test the effect, we covered the background color behind the icon for comparison. Finally, we found that the test result is not ideal and the effect is not good

<!--AnimationDrawable Writing method-->
<!--There is no real machine test. The effect of this writing method looks strange. It may be the problem of the simulator and the preview version-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item android:drawable="@mipmap/ic_launcher" android:duration="600" />
    <item android:drawable="@drawable/api12_logo" android:duration="200" />
    <item android:drawable="@mipmap/ic_launcher" android:duration="200" />
</animation-list>

Dynamic center icon, abnormal

Let's take a look at the smoothing effect in the official documents

Let's use AnimatedVectorDrawable to make dynamic icons,
In order to observe the effect: we open the developer option of the simulator, find the animation duration scaling setting: animation duration x10, and see the effect by ourselves. It is omitted here

Vector image file of smiling face and eye animation 👇👇, Click view to make vector animation online

<!--Only test play. Those interested can make one by themselves-->
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="android:drawable">
        <vector
            android:name="vector"
            android:width="24dp"
            android:height="24dp"
            android:viewportWidth="24"
            android:viewportHeight="24">
            <group android:name="group">
                <path
                    android:name="path_4"
                    android:pathData="M 11.99 2 C 6.47 2 2 6.48 2 12 C 2 17.52 6.47 22 11.99 22 C 17.52 22 22 17.52 22 12 C 22 6.48 17.52 2 11.99 2 Z M 12 20 C 7.58 20 4 16.42 4 12 C 4 7.58 7.58 4 12 4 C 16.42 4 20 7.58 20 12 C 20 16.42 16.42 20 12 20 Z M 12 17.5 C 14.33 17.5 16.32 16.05 17.12 14 L 15.45 14 C 14.76 15.19 13.48 16 12 16 C 10.52 16 9.25 15.19 8.55 14 L 6.88 14 C 7.68 16.05 9.67 17.5 12 17.5 Z"
                    android:fillColor="#FFFFFF"/>
            </group>
            <group android:name="group_1">
                <path
                    android:name="path_1"
                    android:pathData="M 8.5 9.5 M 7 9.5 C 7 9.102 7.158 8.721 7.439 8.439 C 7.721 8.158 8.102 8 8.5 8 C 8.898 8 9.279 8.158 9.561 8.439 C 9.842 8.721 10 9.102 10 9.5 C 10 9.898 9.842 10.279 9.561 10.561 C 9.279 10.842 8.898 11 8.5 11 C 8.102 11 7.721 10.842 7.439 10.561 C 7.158 10.279 7 9.898 7 9.5"
                    android:fillColor="#FFFFFF"/>
                <path
                    android:name="path_3"
                    android:pathData="M 8.5 9.5 M 7 9.5 C 7 9.102 7.158 8.721 7.439 8.439 C 7.721 8.158 8.102 8 8.5 8 C 8.898 8 9.279 8.158 9.561 8.439 C 9.842 8.721 10 9.102 10 9.5 C 10 9.898 9.842 10.279 9.561 10.561 C 9.279 10.842 8.898 11 8.5 11 C 8.102 11 7.721 10.842 7.439 10.561 C 7.158 10.279 7 9.898 7 9.5"
                    android:fillColor="#FFFFFF"/>
            </group>
            <group android:name="group_2">
                <path
                    android:name="path"
                    android:pathData="M 15.5 9.5 M 14 9.5 C 14 9.102 14.158 8.721 14.439 8.439 C 14.721 8.158 15.102 8 15.5 8 C 15.898 8 16.279 8.158 16.561 8.439 C 16.842 8.721 17 9.102 17 9.5 C 17 9.898 16.842 10.279 16.561 10.561 C 16.279 10.842 15.898 11 15.5 11 C 15.102 11 14.721 10.842 14.439 10.561 C 14.158 10.279 14 9.898 14 9.5"
                    android:fillColor="#FFFFFF"/>
                <path
                    android:name="path_2"
                    android:pathData="M 15.5 9.5 M 14 9.5 C 14 9.102 14.158 8.721 14.439 8.439 C 14.721 8.158 15.102 8 15.5 8 C 15.898 8 16.279 8.158 16.561 8.439 C 16.842 8.721 17 9.102 17 9.5 C 17 9.898 16.842 10.279 16.561 10.561 C 16.279 10.842 15.898 11 15.5 11 C 15.102 11 14.721 10.842 14.439 10.561 C 14.158 10.279 14 9.898 14 9.5"
                    android:fillColor="#FFFFFF"/>
            </group>
        </vector>
    </aapt:attr>
    <target android:name="group_1">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="translateX"
                android:duration="1000"
                android:valueFrom="0"
                android:valueTo="7"
                android:valueType="floatType"
                android:interpolator="@android:interpolator/fast_out_slow_in"/>
        </aapt:attr>
    </target>
    <target android:name="group_2">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="translateX"
                android:duration="1000"
                android:valueFrom="0"
                android:valueTo="-7"
                android:valueType="floatType"
                android:interpolator="@android:interpolator/fast_out_slow_in"/>
        </aapt:attr>
    </target>
</animated-vector>

Later, we used AnimationDrawable to test the slow playback effect. If you think about it carefully, can the picture round play effect be good?
Therefore: AnimationDrawable is not recommended. We recommend using AnimatedVectorDrawable to add animation effects to vector graphs.

Question 3: the Android: windowsbackground setting of the Android 12 parent theme is overwritten, and no effect can be seen
Never mind, as long as our UI Designer (artist) designs according to the following size specifications and uses the static center icon, the same effect can be achieved:

  • Center Icon: the margin in the icon content area is 2 / 3 to prevent elements from being cut
  • Brand name Icon: the size scale of the design is 2.5:1

SplashScreen compatible Library

Click to view the official Splash Screen compatibility library document

(A) . dependency Library
Click to view the latest version in the Core library

// Compatible libraries available on all Android versions
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'

(B) . theme and appearance configuration

  • Define the topic that the Activity should use
<style name="Theme.App" parent="Theme.MaterialComponents.xxxxx.DarkActionBar">
        <item name="android:windowBackground">@color/...</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:windowLightStatusBar" tools:targetApi="m">......</item>
        <item name="android:navigationBarColor">@android:color/transparent</item>
    </style>
  • Create a parent theme for the splash screen
<style name="Theme.App.Starting" parent="Theme.SplashScreen.IconBackground">
        <item name="android:windowBackground">@drawable/...</item>
        <item name="windowSplashScreenBackground">@color/...</item>
        <item name="windowSplashScreenAnimationDuration">200</item>
        <item name="postSplashScreenTheme">@style/Theme.App</item>
</style>
  • AndroidManifest.xml configures the theme of the Activity
<manifest>
   <application android:theme="@style/Theme.App.Starting">
    <!-- application and activity,One of two configurations: @style/Theme.App.Starting -->
        <activity android:theme="@style/Theme.App.Starting">
...

(C) initialize SplashScreen

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val splashScreen = installSplashScreen()
        setContent { ...... }
        splashScreen.setKeepVisibleCondition {
            !mainViewModel.mockDataLoading()
        }
        splashScreen.setOnExitAnimationListener(this)
    }

(D) Modify center icon size

<item name="splashScreenIconSize">@dimen/....</item>

(E) . problems encountered
Current problems of compatibility Library

  1. There is no android: windowssplashscreenbrandingimage attribute
  2. If the center icon is configured, it will be cropped into a circle
  3. The default Icon will appear if windowssplashscreenanimated Icon is not configured in the lower version system
  4. Android 12 parent theme setting android:windowBackground is overwritten, and no effect can be seen

Question 1: the reason is the splash under the layout file directory of the compatibility library_ screen_ There is no "view of brand name" in view.xml. Click to view the XML contents of the two layouts:
Under frameworks Splash of Android 12_ screen_ view.xml
Under core splash screen Splash for compatible Libraries_ screen_ view.xml

However, we can still configure the Android: windowssplashscreenbrandingimage attribute in the themes.xml of Android 12, that is, values-v31, because the SplashScreen of Android 12 is integrated into the frameworks;

Problem 2: because the Icon is wrapped in MaskedDrawable in the compatibility library, it will be cut into a circle. The Icon content design should keep 2 / 3 of the inner margin, otherwise the content will be cut out;
How to fix the problem of cutting circles?

Copy the source code, a total of three source code files, copy them, modify and delete them
Alternatively, the icon design criterion is: keep the inner margin of the content to 2 / 3 to prevent elements from being cropped

Question 3: write a transparent drawable.xml and replace it, similar to the following method

<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/transparent"/>
    <size android:width="0dp" android:height="0dp"/>
</shape>

Question 4: the Android: windowsbackground setting of Android 12 parent theme is overwritten, and no effect can be seen
Never mind, as long as our UI Designer (artist) designs according to the following size specifications and uses the static center icon, the same effect can be achieved:

  • Center Icon: the margin in the icon content area is 2 / 3 to prevent elements from being cut
  • Brand name Icon: the size scale of the design is 2.5:1

(F) . make a startup page

  • Mimicking the startup page of Kwai App

You only need to configure the Android: windowsbackground of the parent theme

Android 5.0 ~ Android 11 effect

Since we introduced Android 12 in the article above, we can't set the android:windowBackground of the parent theme for SplashScreen, but we can still achieve the same effect by configuring the static center icon. Please see the following effect:

Android 12 effect

If your UI Designer gives you vector graphics, you can make the center icon move on the Android 12 system 😆 In addition, you can suggest UI designers: unify all systems, launch the "center" icon on the page and display it in the middle, otherwise it will be a little strange

  • Dynamic icon launch page

If it is designed as a dynamic startup icon, two factors need to be considered:

1, The performance effect of the lower version systems is inconsistent. In some systems, the dynamic icon is displayed only when the startup page is closed (this is the case in the personal test Android Tablet 5.1.1 system);
2, How to be compatible with low version systems? First display the brand name at the bottom, and finally wait for the dynamic icon to display?

If you like tossing, you can test more. When I use Android 10.0 to test dynamic icons, the effect looks ok. What is the specific lower limit of the system?
This depends on your product design positioning, and test whether your sister agrees with you and whether the effect conforms to your product;

The recommended approach is:

  • Android 5.0 ~ Android 11.0 systems all use android:windowBackground to configure the startup page background
  • Android12.0 if UI designer has done vector diagram for you, you can make dynamic center icon, not to you, use static icon, also refer to it: analog Kwai App startup page.

In order to display the effect normally on the simulator, when we find the Animator in the developer option of the simulator
Long zoom setting: animation duration x10, slow down 10 times, lack of real machine test 😂😂😂😂😂

Android 12 dynamic startup page icon

Source code analysis

We only analyze Android12 SplashScreen here. There is not much content in the compatibility library, less than 500 lines. Interested students can read it by themselves

We used splashScreen.setOnExitAnimationListener for the first time in XXXActivity. Start from here to the source and initialize SplashScreen in the following method

//android.app.Activity
private SplashScreen getOrCreateSplashScreen() {
    synchronized (this) {
        if (mSplashScreen == null) {
           mSplashScreen = new SplashScreen.SplashScreenImpl(this);
        }
        return mSplashScreen;
    }
}

Let's look at the SplashScreenImpl implementation class

//android.app.Activity

class SplashScreenImpl implements SplashScreen {
    ......
    //Add SplashScreenImpl to this singleton class
    private final SplashScreenManagerGlobal mGlobal;
    public SplashScreenImpl(Context context) {
        mGlobal = SplashScreenManagerGlobal.getInstance();
    }
    @Override
    public void setOnExitAnimationListener(@NonNull SplashScreen.OnExitAnimationListener listener) {
        ......
        mGlobal.addImpl(this); // It is used for the callback that the startup screen will exit later
    }
    ......
    public void setSplashScreenTheme(@StyleRes int themeId) {
        ......
        try {
            //Set the theme of the splash screen
            AppGlobals.getPackageManager().setSplashScreenTheme(......);
        } catch (RemoteException e) {
            Log.w(TAG, "Couldn't persist the starting theme", e);
        }
    }
}

// Splash screen manager
class SplashScreenManagerGlobal {
    ......
    // Manage multiple flash screen implementations
    private final ArrayList<SplashScreenImpl> mImpls = new ArrayList<>();

    private SplashScreenManagerGlobal() {
        // Register the splash screen manager with this process
        ActivityThread.currentActivityThread().registerSplashScreenManager(this);
    }
    ......
    
    private static final Singleton<SplashScreenManagerGlobal> sInstance =
    new Singleton<SplashScreenManagerGlobal>() {
        @Override
        protected SplashScreenManagerGlobal create() {
            return new SplashScreenManagerGlobal();
        }
    };

    private void addImpl(SplashScreenImpl impl) {
        synchronized (mGlobalLock) {
            mImpls.add(impl);
        }
    }

    private void removeImpl(SplashScreenImpl impl) {
        synchronized (mGlobalLock) {
            mImpls.remove(impl);
        }
    }
    ......
    public void handOverSplashScreenView(IBinder token,SplashScreenView splashScreenView) {
        //Called = > splashscreenview. Transfersurface();
        transferSurface(splashScreenView);
        //Callback = > impl.mexitanimationlistener.onslashscreenexit (view);
        dispatchOnExitAnimation(token, splashScreenView);
    }
    ......
}

We see that when initializing SplashScreenManagerGlobal, the splash screen manager is registered with this process

//android.app.ActivityThread

public void registerSplashScreenManager(SplashScreen.SplashScreenManagerGlobal manager) {
    synchronized (this) {
        mSplashScreenGlobal = manager;
    }
}

How to add SplashScreen to the current window?
ActivityThread inherits ClientTransactionHandler, which has such an abstract method:

//android.app.ClientTransactionHandler
public abstract void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
            @NonNull SplashScreenViewParcelable parcelable);

ActivityThread will certainly implement this method, so who called it? Due to the space problem, it is not necessary to introduce and analyze the code line by line. Interested students can study it in depth. Let's post the call flow chart below

Who called handleAttachSplashScreenView(), and its call chain flow chart is as follows:

OK, after reading the flowchart, let's take another look at the ActivityThread#handleAttachSplashScreenView

//android.app.ActivityThread

@Override
public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
@Nullable SplashScreenView.SplashScreenViewParcelable parcelable) {
    final DecorView decorView = (DecorView) r.window.peekDecorView();
    if (parcelable != null && decorView != null) {
        createSplashScreen(r, decorView, parcelable);
    }
    ......
}

private void createSplashScreen(ActivityClientRecord r, DecorView decorView,
 SplashScreenView.SplashScreenViewParcelable parcelable) {
    // Initializing the SplashScreenView builder
    final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);
    // Get the configuration data from the parcelable, initialize the SplashScreenView through build() and set the data
    final SplashScreenView view = builder.createFromParcel(parcelable).build();
    // Add SplashScreenView to DecorView
    decorView.addView(view);
    // Set SystemUI color
    view.attachHostActivityAndSetSystemUIColors(r.activity, r.window);
    // refresh the view
    view.requestLayout();
    ......
}

The core part of the source code has been analyzed almost, and the rest of the source code can be viewed and read by interested students themselves

summary

  • compileSdk upgrade to 31
  • The compatible library SplashScreen is used uniformly in the product
implementation 'androidx.core:core-splashscreen:Latest version'
  • Resource directory in demo example
drawable - Define lower version drawable resources
drawable-v23 - Definition 6.0 Above resources
drawable-v31 - definition Android12 And above resources
values - Define default resources
values-night - Define default dark mode resources
values-v23 - Definition 6.0 Above system resources
values-v31 - definition Android12 And above resources
values-night-v31 - definition Android12 And above
  • Launch page icon design criteria

In the large picture of the center icon, the content needs to keep 2 / 3 of the inner margin, otherwise the icon will be cut off. In addition, the size of the icon can be modified;
Bottom brand name Icon: the size scale needs to be 2.5:1
The compatibility library SplashScreen lower version does not support setting the bottom brand icon,
Android12 needs to manually add the following attributes in the values-v31 directory to display the brand name icon;

<!--The compatibility library does not have this attribute. We need to values-v31 Configure it separately-->
<item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
  • android:windowBackground can be used to set the window background for the parent theme in the following Android 12 systems. Remember not to set the window background of the parent theme in Android 12 and above systems (because it has no effect) 😅😅)
  • For Android 12 system and the following systems, if you use Android: Windows background, you must set a transparent drawable for windows splash screen animated icon, otherwise the robot icon will appear
  • You must pay attention to the color of windowssplashscreenbackground. If there is a problem with the configuration, this color will flash when the startup page transitions to the main page. It is recommended to configure the same color as Android: windowsbackground of Activity
  • Add an "advertisement or promotion page" on the startup screen. The code and effect are as follows:
override fun onSplashScreenExit(splashScreenViewProvider: SplashScreenViewProvider) {
    if(splashScreenViewProvider.view is ViewGroup){
        // Add a launch page, advertisement or promotion page here
        val composeView = ComposeView(this@SplashScreenCompatActivity).apply {
            setContent {
                SplashAdScreen(onCloseAd = {
                    splashScreenViewProvider.remove()
                })
            }
        }
        (splashScreenViewProvider.view as ViewGroup).addView(composeView)
        return
    }
}


Practice - Launch page add advertising or promotion page

Reference address

All sample source code GitHub: https://github.com/yugu88/SplashScreens

Tags: Android kotlin

Posted on Tue, 19 Oct 2021 14:49:35 -0400 by NoviceJ