新公司采用React Native开发,所以就顺利入坑了…
React Native启动白屏是一个很普遍但又很严重的问题,网上也有很多文章,这里就此问题,从分析到常用的解决方案做一个简单的总结。
先看图,白屏的现象:
图中手机为ZTE星星2号(专用测试机,为嘛?因为公司没给配啊,还有自己买的,所以就是专用的喽),Andriod 4.4的,可以看到白屏现象很严重,最后用自己的华为mate9,Android 8.0系统进行了测试,依然存在白屏的现象。
网上的解决方案,大概分为两种:
对ReactRootView以及ReactInstanceManager进行预加载,也就是说在本地配置一个启动界面,然后开始预加载RN的核心引擎,等完毕后直接跳转到RN的主界面,RN的主界面拿到初始化好的ReactRootView直接进行加载。这种方案,自己尝试了下,并不可行,只是减少了白屏的显示时间,实际上依然会有一小段时间的白屏现象。另外,采用这种方式,RN中组件的生命周期会得不到正确的执行。
在白屏上预先覆盖一张图片用于遮掩白屏,等到RN相关的ReactRootView初始化完毕后再移除掉,作者的博客:https://www.jianshu.com/p/78571e5435ec 此方案可行,测试了一下,确实解决了白屏的问题,但是我没选择此方案,因为我觉得还不够完美。
下面就这两个方案做一下简单的分析。
预加载方式
我的React Native 版本号为0.53.3,ReactActivity是RN的入口
ReactActivity源码
public abstract class ReactActivity extends Activity implements DefaultHardwareBackBtnHandler,PermissionAwareActivity { private final ReactActivityDelegate mDelegate; protected ReactActivity() { //构造中初始化相关的委派类 mDelegate = createReactActivityDelegate(); } /** * Called at construction time,override if you have a custom delegate implementation. */ protected ReactActivityDelegate createReactActivityDelegate() { return new ReactActivityDelegate(this,getMainComponentName()); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //调用了委派类的onCreate方法 mDelegate.onCreate(savedInstanceState); } }
ReactActivityDelegate源码
public class ReactActivityDelegate { public ReactActivityDelegate(Activity activity,@Nullable String mainComponentName) { mActivity = activity; mMainComponentName = mainComponentName; mFragmentActivity = null; } public ReactActivityDelegate( FragmentActivity fragmentActivity,@Nullable String mainComponentName) { mFragmentActivity = fragmentActivity; mMainComponentName = mainComponentName; mActivity = null; } protected @Nullable Bundle getLaunchOptions() { return null; } protected ReactRootView createRootView() { return new ReactRootView(getContext()); } protected ReactNativeHost getReactNativeHost() { return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost(); } public ReactInstanceManager getReactInstanceManager() { return getReactNativeHost().getReactInstanceManager(); } protected void onCreate(Bundle savedInstanceState) { if (mMainComponentName != null) { //mMainComponentName一定不会为空,所以一定会执行loadApp方法 loadApp(mMainComponentName); } mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); } //RN的白屏问题就是出现在这里,两个比较耗时的函数分别为getReactNativeHost().getReactInstanceManager()和mReactRootView.startReactApplication, //预先缓存就是针对这里的,把这一步的操作,提前完成,然后用的时候,直接从内存开始加载 protected void loadApp(String appKey) { if (mReactRootView != null) { throw new IllegalStateException("Cannot loadApp while app is already running."); } mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(),appKey,getLaunchOptions()); getPlainActivity().setContentView(mReactRootView); } }
ReactRootViewCacheManager工具类源码
public class ReactRootViewCacheManager { private static HashMap<String,ReactRootView> rootViewHashMap = new HashMap<>(); /** * 预加载RN渲染引擎 * @param activity */ public static void init(Activity activity,String moduleName){ //包含了,立刻返回 if(rootViewHashMap.containsKey(moduleName))return; //ReactRootView 直接new就可以了 ReactRootView mReactRootView = new ReactRootView(activity); //在MainApplication中提前初始化ReactInstanceManager ReactInstanceManager reactInstanceManager = ((MainApplication) activity.getApplication()).getReactNativeHost().getReactInstanceManager(); //第一个参数为ReactInstanceManager //第二个参数建议和RN主界面中getMainComponentName返回的内容一样,也就是此函数中的第二个形参moduleName //第三个参数为null即可 mReactRootView.startReactApplication(reactInstanceManager,moduleName,null); rootViewHashMap.put(moduleName,mReactRootView); } /** * 获取指定的reactrootview * @param moduleName * @return */ public static ReactRootView getReactRootView(String moduleName){ ReactRootView reactRootView = rootViewHashMap.get(moduleName); if(reactRootView != null){ ViewParent parent = reactRootView.getParent(); if(parent != null && parent instanceof ViewGroup){ ((ViewGroup)parent).removeAllViews(); } } return reactRootView; } }
大概流程就是在本地的SplashActivity的onCreate中先调用ReactRootViewCacheManager.init方法,完成后在执行跳转到RN主界面即可,这里需要注意的细节,我们需要简单修改下ReactActivity的源码和ReactActivityDelegate的源码,怎么办?很简单,复制ReactActivity的源码到新创建的BaseReactActivity中,复制ReactActivityDelegate到BaseReactActivityDelegate中,并且把BaseReactActivity中的ReactActivityDelegate全部替换成BaseReactActivityDelegate即可。
SplashActivity源码
public class SplashActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ReactRootViewCacheManager.init(this,MainActivity.COMPONENT_NAME); //因为ui线程是从上到下执行的,所以到这里了,就表示预加载成功 startActivity(new Intent(this,MainActivity.class)); } }
BaseReactActivityDelegate源码只关注loadApp函数
protected void loadApp(String appKey) { if (mReactRootView != null) { throw new IllegalStateException("Cannot loadApp while app is already running."); } String mainComponentName = ((BaseReactActivity) getPlainActivity()).getMainComponentName(); //从缓存中读取初始化好的ReactRootView,如果不为空直接进行加载 ReactRootView reactRootView = ReactRootViewCacheManager.getReactRootView(mainComponentName); if(reactRootView == null){ mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(),getLaunchOptions()); getPlainActivity().setContentView(mReactRootView); }else{ getPlainActivity().setContentView(reactRootView); } }
第二种方案
此方案不做代码演示了,相信大家已经明白其中的流程了,说下他存在的问题,依然会有白屏现象,只不过这白屏现象不是RN造成的,而是安卓App本身就有白屏。
自己使用的方案就是解决安卓app启动白屏的思路,用到自定义主题,也就是android:windowBackground的属性。
<!-- 主题的方式解决rn启动白屏问题 --> <style name="AppTheme.Main" parent="TranslucentTheme" > <item name="android:windowBackground">@drawable/splash</item> </style> <activity //配置主题 android:theme="@style/AppTheme.Main" android:name=".activity.MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:label="@string/app_name" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
看下最终效果图
遗留的问题和第二个解决方案类似,无法自由控制白屏的时间。
原文链接:https://www.f2er.com/react/301673.html