前段时间在项目中遇到了一个问题:从原生模块跳转到RN模块时会有一段短暂的白屏时间,特别是在低端手机更加明显。在网上搜了一圈,发现这个问题非常常见。
ReactRootView mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
这两行代码就是白屏的主要原因。因为这两行代码把jsbundle文件读入到内存中,这个过程肯定是需要耗费一些时间的,当jsbundle文件越大,可以预见加载到内存中需要的时间就越长。
解决办法就是以空间换时间,在app启动时候,就将ReactRootView初始化出来,并缓存起来,在用的时候从缓存获取ReactRootView使用,达到秒开。
目前的React Native版本更新到了0.45.1,而网上大部分的解决方案都偏旧,但是解决思路还是一样的,不过具体的解决方法会做些修改(因为RN源码的变动)。
下面开始详细说明。
1. 创建ReactRootView缓存管理器
View缓存管理器先提前将ReactRootView初始化并用一个WeakHashMap保存。在这里需要十分小心内存泄露的问题。
public class RNCacheViewManager {
public static Map<String, ReactRootView> CACHE;
public static final int REQUEST_OVERLAY_PERMISSION_CODE = 1111;
public static final String REDBOX_PERMISSION_MESSAGE =
"Overlay permissions needs to be granted in order for react native apps to run in dev mode";
public static ReactRootView getRootView(String moduleName) {
if (CACHE == null) return null;
return CACHE.get(moduleName);
}
public static ReactNativeHost getReactNativeHost(Activity activity) {
return ((ReactApplication) activity.getApplication()).getReactNativeHost();
}
/**
* 预加载所需的RN模块
* @param activity 预加载时所在的Activity
* @param launchOptions 启动参数
* @param moduleNames 预加载模块名
* 建议在主界面onCreate方法调用,最好的情况是主界面在应用运行期间一直存在不被关闭
*/
public static void init(Activity activity, Bundle launchOptions, String... moduleNames) {
if (CACHE == null) CACHE = new WeakHashMap<>();
boolean needsOverlayPermission = false;
if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(activity)) {
needsOverlayPermission = true;
Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.getPackageName()));
FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
Toast.makeText(activity, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
activity.startActivityForResult(serviceIntent, REQUEST_OVERLAY_PERMISSION_CODE);
}
if (!needsOverlayPermission) {
for (String moduleName : moduleNames) {
ReactRootView rootView = new ReactRootView(activity);
rootView.startReactApplication(
getReactNativeHost(activity).getReactInstanceManager(),
moduleName,
launchOptions);
CACHE.put(moduleName, rootView);
FLog.i(ReactConstants.TAG, moduleName+" has preload");
}
}
}
/**
* 销毁指定的预加载RN模块
*
* @param componentName