React Native 实现无白屏启动页
一个 React Native 应用在启动的时候往往不如原生快,这主要是因为 RN 比原生应用多了一层 JS Bridge,需要的初始化的时间也就更长一些。所以如果不做任何改动的话,我们打开应用的瞬间就会观察到短暂的白屏现象。为了更好的用户体验,我们需要想办法去掉这个讨厌的白屏,使得应用在打开时就显示启动页。
本文主要讨论如何使用 react-native-splash-screen 在 Android 和 iOS 上实现无白屏启动页效果。
Android 上启动页的实现
现有方案的对比
通常,在 Android 上,我们会为启动页设置以下 Theme:
<style name="AppTheme.SplashTheme" parent="AppTheme">
<item name="android:windowBackground">@drawable/start_page_background</item>
<-- or even more -->
<item name="colorPrimaryDark">@android:color/white</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
通过添加 android:windowBackground
来设置启动页背景图,然后再在 SplashScreenActivity 中做路由跳转。但是这在 RN 中显然行不通,因为 RN 里只有一个 MainActivity
。那该怎么办呢?
网上的文章中,有一种方案是通过自己配置一个启动页 activity,在其中对 ReactRootView
以及 ReactInstanceManager
进行预加载,加载完毕后再跳转到 MainActivity
。这种做法会有一些副作用,而且也并非完美的解决方案,需要对 ReactActivityDelegate
和 ReactActivityDelegate
进行改造。
react-native-splash-screen 的作者提供了一种思路,只要在根视图上添加一个 dialog 遮盖掉白屏,然后等到合适的时机(JS Bundle 加载并渲染完毕后)再隐藏掉这个 dialog。
这种方法还是非常巧妙的,因为我们不需要对源码做修改,所以不用担心随着 RN 版本的迭代更新而失效,而且也不会对 RN 组件的生命周期造成影响。
Android 实现细节
安装 react-native-splash-screen 之后,我们需要添加一个开屏页的布局 launch_screen.xml
,理论上来说可以自由添加任何元素,但是不推荐添加过于复杂的布局,一般来说添加一张图片就足够了:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/bg_splash_screen" />
</RelativeLayout>
之后我们还可以定制化开屏页的主题,如下:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- use this to delay app launch from a cold start to eliminate blank space -->
<!-- which is not a best practice in splash screen, not recommended -->
<!--<item name="android:windowIsTranslucent">true</item>-->
<!-- use this to specify the status bar color -->
<item name="colorPrimaryDark">@color/primary_dark</item>
<!-- use this to replace the blank from a cold start -->
<item name="android:windowBackground">@drawable/splash</item>
</style>
</resources>
这里有两种选项,一种是直接跳过冷启动的白屏,另一种是替换默认背景图,推荐使用第二种方式。
然后将主题应用到开屏页:
public class MainActivity extends ReactActivity {
// other code...
@Override
protected void onCreate(Bundle savedInstanceState) {
// you have to add it before super.onCreate
SplashScreen.show(this, R.style.AppTheme);
super.onCreate(savedInstanceState);
}
}
这里我将开屏页的主题和 App 主题设置为同一个,这样状态栏颜色就不会发生变化。因为我发现如果开屏页和 App 主题的状态栏颜色不一致的话,会造成启动时状态栏从 App 主题颜色过渡到启动页状态栏颜色,进入主页面后再过渡到 App 主题,效果不是很好。
看下效果:
splash_screen_android.gif由于设置了背景图,冷启动时会观察到从背景图切换到启动页图的效果。
iOS 上启动页的实现
iOS 实现原理与 Android 类似,通过添加一个 Launch Image 或者修改 LaunchScreen.xib 来控制启动屏的显示内容,然后等到 JS Bundle 加载完再显示主页面。
这里我同样通过添加启动页图片来实现,xCode 中选择 Images.xcassets,然后 + → App Icons & Launch Images → New iOS Launch Image,选中 LaunchImage 并在右侧的 Attribute Inspector 中选择你需要支持的屏幕。最后将准备好的启动页图片拖到对应的型号下就可以了。附上启动页图片大小参照:Static Launch Screen Images
添加完启动页图片后,还需要修改以我们新添加的图片作为启动页,在 xCode 中选择 General → App Icons and Launch Images → Launch Images Srouce → Use Asset Catalog,再选择我们新添加的 LaunchImage 就可以了。注意可能要将 app 卸载后重新安装,我们修改后的启动页图片才可能生效。
效果图如下:
splash_screen_ios.gif可以看到 iOS 上有一点和 Android 不同,由于 iOS 没有冷启动机制,所以不会出现背景图切换的问题。