原文地址:ReactNative安卓首屏白屏优化-githubjava
公司现有app中部分模块使用reactnative开发,在实施的过程当中,rn良好的兼容性,极佳的加载、动画性能,提高了咱们的开发、测试效率,提高了用户体验。react
可是,在android中,当点击某个rn模块的入口按钮,弹出rn的activity到rn的页面展示出来的过程当中,会有很明显的白屏现象,不一样的机型不一样(cpu好的白屏时间短),大概1s到2s的时间。android
注意,只有在真机上才会有此现象,在模拟器上没有此现象彻底是秒开。ios上也是秒开,测试的最低版本是ios7,iphone4s。ios
reactnative版本0.20.0。git
jsbundle文件大小717kb。github
通过了大量的源码阅读,和网上资料查找,最终完美的解决了这个问题,不管什么机型,均可以达到秒开,如图(虽然下图是模拟器的截图,可是真机效果基本同样):浏览器
通常优化速度问题,首先就是要找到时间分布,而后根据二八原则,先优化耗时最长的部分。缓存
android集成rn都会继承官方提供的ReactActivityapp
public class MainActivity extends ReactActivity {
而后只在本身的activity中覆盖一些配置项。iphone
在官方的ReactActivity中的onCreate方法中
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(); } } mReactInstanceManager = createReactInstanceManager(); ReactRootView mReactRootView = createRootView(); mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions()); setContentView(mReactRootView); }
最慢的就是这两行代码,占了90%以上的时间。
ReactRootView mReactRootView = createRootView(); mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
这两行代码就是把jsbundle文件读入到内存中,并进行执行,而后初始化各个对象。
在app启动时候,就将mReactRootView初始化出来,并缓存起来,在用的时候直接setContentView(mReactRootView),达到秒开。
缓存rootview管理器主要用于初始化和缓存rootview对象。
import android.app.Activity; import android.os.Bundle; import android.view.ViewParent; import com.facebook.react.LifecycleState; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactPackage; import com.facebook.react.ReactRootView; import java.lang.reflect.Field; /** * 缓存view管理 */ public class RNCacheViewManager { private static ReactRootView mRootView = null; private static ReactInstanceManager mManager = null; private static AbsRnInfo mRnInfo = null; //初始化 public static void init(Activity act, AbsRnInfo rnInfo) { init(act, rnInfo, null); } public static void init(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) { if (mManager == null) { updateCache(act, rnInfo, launchOptions); } } public static void updateCache(Activity act, AbsRnInfo rnInfo) { updateCache(act, rnInfo, null); } //更新cache,适合于版本升级时候更新cache public static void updateCache(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) { mRnInfo = rnInfo; mManager = createReactInstanceManager(act); mRootView = new ReactRootView(act); mRootView.startReactApplication(mManager, rnInfo.getMainComponentName(), launchOptions); } //设置模块名称,由于是private,只能经过反射赋值 public static void setModuleName(String moduleName) { try { Field field = ReactRootView.class.getDeclaredField("mJSModuleName"); field.setAccessible(true); field.set(getReactRootView(), moduleName); } catch (Throwable e) { throw new RuntimeException(e); } } //设置启动参数,由于是private,只能经过反射赋值 public static void setLaunchOptions(Bundle launchOptions) { try { Field field = ReactRootView.class.getDeclaredField("mLaunchOptions"); field.setAccessible(true); field.set(getReactRootView(), launchOptions); } catch (Throwable e) { throw new RuntimeException(e); } } public static ReactRootView getReactRootView() { if(mRootView==null){ throw new RuntimeException("缓存view管理器还没有初始化!"); } return mRootView; } public static ReactInstanceManager getReactInstanceManager() { if(mManager==null){ throw new RuntimeException("缓存view管理器还没有初始化!"); } return mManager; } public static AbsRnInfo getRnInfo() { if(mRnInfo==null){ throw new RuntimeException("缓存view管理器还没有初始化!"); } return mRnInfo; } public static void onDestroy() { try { ViewParent parent = getReactRootView().getParent(); if (parent != null) ((android.view.ViewGroup) parent).removeView(getReactRootView()); } catch (Throwable e) { e.printStackTrace(); } } public static void clear() { try { if (mManager != null) { mManager.onDestroy(); mManager = null; } if (mRootView != null) { onDestroy(); mRootView = null; } mRnInfo = null; } catch (Throwable e) { e.printStackTrace(); } } private static ReactInstanceManager createReactInstanceManager(Activity act) { ReactInstanceManager.Builder builder = ReactInstanceManager.builder() .setApplication(act.getApplication()) .setJSMainModuleName(getRnInfo().getJSMainModuleName()) .setUseDeveloperSupport(getRnInfo().getUseDeveloperSupport()) .setInitialLifecycleState(LifecycleState.BEFORE_RESUME); for (ReactPackage reactPackage : getRnInfo().getPackages()) { builder.addPackage(reactPackage); } String jsBundleFile = getRnInfo().getJSBundleFile(); if (jsBundleFile != null) { builder.setJSBundleFile(jsBundleFile); } else { builder.setBundleAssetName(getRnInfo().getBundleAssetName()); } return builder.build(); } }
将官方的ReactActivity粘出来,重写2个方法,onCreate和onDestroy,其他代码不动。
onCreate方法中使用缓存rootview管理器来得到rootview对象,而不是从新建立。
这里曾尝试继承ReactActivity,而不是重写这个类,可是子类覆盖onCreate方法时候,必需要调用super.onCreate,不然编译会报错,可是super.onCreate方法会从新建立rootview,因此实在是绕不过去了。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (RNCacheViewManager.getRnInfo().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(); } } mReactInstanceManager = RNCacheViewManager.getReactInstanceManager(); ReactRootView mReactRootView = RNCacheViewManager.getReactRootView(); setContentView(mReactRootView); }
onDestroy方法中,不能再调用原有的mReactInstanceManager.destroy()方法了,不然rn初始化出来的对象会被销毁,下次就用不了了。同时,要卸载掉rootview的parent对象,不然下次再setContentView时候回报错。
protected void onDestroy() { RNCacheViewManager.onDestroy(); super.onDestroy(); }
RNCacheViewManager.onDestroy的方法:
public static void onDestroy() { try { ViewParent parent = getReactRootView().getParent(); if (parent != null) ((android.view.ViewGroup) parent).removeView(getReactRootView()); } catch (Throwable e) { e.printStackTrace(); } }
RNCacheViewManager.init((Activity) context, new RnInfo(moduleName, launchOptions));
其中RnInfo以下:
public class RnInfo extends AbsRnInfo { private String mModuleName; private Bundle mLaunchOptions; public RnInfo(String moduleName) { this.mModuleName = moduleName; } public RnInfo(String moduleName, Bundle launchOptions) { this.mModuleName = moduleName; this.mLaunchOptions = launchOptions; } @Nullable @Override public Bundle getLaunchOptions() { return mLaunchOptions; } @Override public String getMainComponentName() { return mModuleName; } @Override public String getJSMainModuleName() { return RNKeys.Default.DEf_JS_MAIN_MODULE_NAME; } @Nullable @Override public String getJSBundleFile() { return RNManager.getJsBundlePath(); } @Override public boolean getUseDeveloperSupport() { return true; } @Override public List<ReactPackage> getPackages() { return Arrays.asList( new MainReactPackage(), new BBReactPackage() ); } }
但愿本篇文档能帮助遇到相似问题的小伙伴们。
reactnative虽然不是银弹,可是在目前移动端浏览器兼容性弱爆了的状况下,仍是能极大的提高开发测试效率的,性能也是极佳的,看好rn的将来。
http://zhuanlan.zhihu.com/magilu/2058748...