This article is from the React Native Learning Notes series.

Problem Description:

Both Android and iOS apps that use the React Native architecture display a blank screen at startup for about 1 to 3s (depending on the performance of the phone or emulator).

Problem analysis:

React Native reads the JS bundle into memory when it starts and renders it. During this time, the JS bundle has not been loaded and rendered, so the screen is blank.

A white screen feels unfriendly, but is there a way not to display it?

Here’s why React Native displays a blank screen when it starts up. Now that we know the cause of the problem, we are not far away from solving it. Most of the APP is launched on the market when there’s a startup screen, start screen is more friendly for users, which show welcome message, and show some product information or some advertising, start page for program is for initialization program to complete loading the data, the retained time to do some initialization, startup screen waiting time can be long or short, It depends on the business.

Here’s how to add a startup screen to React Native Android and fix the problem.

Add a startup screen for React Native Android

In order to add a startup screen to React Native Android, we need to make a change to React Native. Let’s start with the source code.

The principle of analysis

The Android part of an application that is initialized with a React-Native init has only one MainActivity, which is the entry point to the entire Android application.

public class MainActivity extends ReactActivity { /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "GitHubPopular"; }}Copy the code

As you can see from the above code, MainActivity is clean, with just a getMainComponentName() method. It is clear that the blank screen is not caused by MainActivity.

Next, let’s continue our exploration by entering the ReactActivity source code.

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) { // Get permission to show redbox in dev builds. if (! Settings.canDrawOverlays(this)) { Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(serviceIntent); FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE); Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); } } mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), getMainComponentName(), getLaunchOptions()); setContentView(mReactRootView); mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); }Copy the code

The code above is for the onCreate method of the ReactActivity, which acts as an entry point to the Activity and is responsible for the initialization of the program, among other things. Those familiar with Android development know that the onCreate method uses setContentView() to set up an interface for user interaction. SetContentView () is also used in the onCreate method of the ReactActivity.

 mReactRootView = createRootView();
 
 mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      getMainComponentName(),
      getLaunchOptions());
 setContentView(mReactRootView);
Copy the code

In the above code, the first call is mReactRootView = createRootView(); Create a root view that is the top view of the React Native app. Then through mReactRootView startReactApplication method, load and render js bundle, this process is time-consuming. Finally, setContentView(mReactRootView); Bind the root view to the Activity interface.

That’s the basic principle, so let’s take a knife to ReactActivity.

Implementation approach

Here’s the idea:

  1. Control the ReactActivity to display the startup screen when the APP starts.
  2. Provides a public interface to turn off the startup screen.
  3. The public interface is called at the appropriate js bit (usually after the program initialization is complete) to turn off the startup screen.

The specific implementation

Step 1: Control the ReactActivity to display the startup screen when the APP starts

We need to do some preparatory work before cutting into the ReactActivity.

Basic preparation:

First, we need to make a copy of the ReactActivity. Because ReactActivity is part of the React Native source code, we can’t modify it directly, so we need to make a copy of it. Then change the MainActivity inheritance to the ReactActivity that we copied.

Secondly. Modify the getUseDeveloperSupport method.

Because the getUseDeveloperSupport method of ReactNativeHost is of protected type, we cannot access it outside of the package to which it belongs. But we also need to call this method in the ReactActivity, so we can use reflection to satisfy our need.

protected boolean getUseDeveloperSupport() {
    ReactNativeHost rnh=((ReactApplication) getApplication()).getReactNativeHost();
        Classcls=rnh.getClass();
    Object support= null;
    try {
        Method method = cls.getDeclaredMethod("getUseDeveloperSupport", new Class[]{});
        method.setAccessible(true);
        support=method.invoke(rnh);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return (boolean) support;
} 
Copy the code

The preliminary work is ready to play, now let’s get started.

To make the ReactActivity display the startup screen, we need to create a View container that holds both the startup screen View and the React Native root View.

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) { // Get permission to show redbox in dev builds. if (! Settings.canDrawOverlays(this)) { Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(serviceIntent); FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE); Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); } } mRootView = new FrameLayout(this); splashView = LayoutInflater.from(this).inflate(R.layout.launch_screen, null); mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), getMainComponentName(), getLaunchOptions()); mRootView.addView(mReactRootView); mRootView.addView(splashView, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); setContentView(mRootView); mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); }Copy the code

First, I created onemRootView = new FrameLayout(this);View container.

Second, read the startup screen layout file into memorysplashView = LayoutInflater.from(this).inflate(R.layout.launch_screen, null);.

Again, addmReactRootViewwithsplashView, pay attention to the order of addition.

Finally, themRootViewBind to the Activity.

In this way, we control the ReactActivity to display the welcome screen when it starts. Next we need to open and close the ReactActivity and use the interface method instead.

/ * * * hidden startup screen * / public void hide () {if (mRootView = = null | | splashView = = null) return; AlphaAnimation fadeOut = new AlphaAnimation(1, 0); fadeOut.setDuration(1000); splashView.startAnimation(fadeOut); fadeOut.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mRootView.removeView(splashView); splashView = null; } @Override public void onAnimationRepeat(Animation animation) { } }); }Copy the code

In the above method, a fade animation is added for 1s to make the welcome screen and other screens more natural. This is not enough, because we need to call the hide method in JS and control the closure of the welcome screen. Js cannot directly call Java, so we need to build a bridge for them (Native Modules).

First, create oneReactContextBaseJavaModuleType for js calls.

/** * LaunchScreenModule * http://www.cboy.me * GitHub:https://github.com/crazycodeboy * Eamil:[email protected] */ public class LaunchScreenModule extends ReactContextBaseJavaModule{ public LaunchScreenModule(ReactApplicationContext reactContext) {  super(reactContext); } @Override public String getName() { return "LaunchScreen"; } @ReactMethod public void hide(){ getCurrentActivity().runOnUiThread(new Runnable() { @Override public void run() { ((ReactActivity)getCurrentActivity()).hide(); }}); }}Copy the code

Next, create oneReactPackageType class to register our React NativeLaunchScreenModuleComponents.

/** ** LaunchScreenReactPackage http://www.cboy.me * GitHub:https://github.com/crazycodeboy * Eamil:[email protected] */ public class LaunchScreenReactPackage implements ReactPackage { @Override public List> createJSModules() { return Collections.emptyList(); } @Override public List createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List createNativeModules( ReactApplicationContext reactContext) { List modules = new ArrayList<>(); modules.add(new LaunchScreenModule(reactContext)); return modules; }}Copy the code

Again, register in MainApplicationLaunchScreenModuleComponents.

@Override protected List getPackages() { return Arrays.asList( new MainReactPackage(), new LaunchScreenReactPackage() );  }Copy the code

Finally, call LaunchScreenModule in JS.

Create a file called LaunchScreen and add the following code.

/** * Android LaunchScreen * from: http://www.cboy.me * GitHub:https://github.com/crazycodeboy * Eamil:[email protected] * @flow */ 'use strict'; import { NativeModules } from 'react-native'; module.exports = NativeModules.LaunchScreen;Copy the code

The purpose of this code is to expose the LaunchScreen module to JS.

We can now turn off the LaunchScreen by calling the hide() method of LaunchScreen in JS.

Don’t forget to import LaunchScreen from ‘./LaunchScreen.

React Native Android starts with a blank screen. If you have any questions, you can add 165774887 to the group and discuss with me.

In addition, I would like to share with you a solution to flash a white or black screen when Android starts. This is an Android issue that has nothing to do with React Native. Read on.

Modify theme to solve flash white screen/black screen

Problem Description:

There are a lot of apps on the market that flash a black screen or a white screen when they launch, and some apps don’t. The reason is that the theme is playing tricks.

Problem analysis

When an app icon is clicked, Android creates a process for the clicked app, then creates an Application instance, then applies the theme, and then starts the Activity. Since it takes time to start an Activity, the interval between these is the time it takes to flash a white screen or a black screen.

The solution

To solve the problem of flashing a white or black screen at startup, we can start with a theme and create a transparent theme for the app.

Step 1: Create a transparent theme.

<! - set the transparent background - > < item name = "android: windowIsTranslucent" > true < / item >Copy the code

Step 2: Apply the theme for the application in androidmanifest.xml.

 
Copy the code

That way, it won’t flash a black or white screen at startup.

If your application needs a specific theme, but the theme is not transparent, you can first set the default theme of application to be transparent, and then after the application starts (this can be done in the launch page), Use the public void setTheme(int resid) method to set the theme to the theme you want.

The last

Now that I have come, please leave me a favor and encourage me to continue to create

If you like my articles, follow mineBlog @ http://www.cboy.me/Come on, let’s be friends

Click here and follow:

GitHub: My open source project