前言
公司现有app中部分模块使用reactnative开发,之前使用的都是webview来加载H5页面,在实施的过程中,reactnative良好的兼容性,极佳的加载、动画性能,提升了我们的开发、测试效率,提升了用户体验。
但是,在android中,当点击某个rn模块的入口按钮,弹出rn的activity到rn的页面展现出来的过程中,会有很明显的白屏现象,不同的机型不同(cpu好的白屏时间短),大概1s到2s的时间。这是因为每次加载reactAcivity都是会去重新加载js资源包,初始化,导致加载时间过长。
分析问题
一般优化速度问题,首先就是要找到时间分布,然后根据二八原则,先优化耗时最长的部分。android集成rn都会继承官方提供的ReactActivity。
public class MainActivity extends ReactActivity {
然后只在自己的activity中覆盖一些配置项。
在官方的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%以上的时间。
mReactInstanceManager = createReactInstanceManager();
ReactRootView mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
第一行是创建React管理的实例 把jsbundle文件读入到内存中。
第二行是创建React页面。
第三行是初始化React页面。
解决方案
package com.haier.hairy.react;
import android.app.Activity;
import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.framework.util.Preferences;
import com.microsoft.codepush.react.CodePush;
import java.util.Arrays;
/**
* React管理缓存类
* Created by fww
* date 16/6/20.
*/
public class RNCacheManager {
private static ReactInstanceManager mManager = null;
private static RNCacheManager mInstance;
private static CodePush codePush;
public static RNCacheManager getInstance(Activity activity){
if(mInstance==null){
mInstance = new RNCacheManager();
codePush = new CodePush(Preferences.DEBUG?/*"1RMMWRaTi1WhB8z1CEjH3B5aRztnEJTjrzq6l"*/"iTJagF-2Ox8EJAp6kfQ5JO7hHoM8EJTjrzq6l":"GYGm87k_sh4ObCJwrI5qNH-i_9f9EJTjrzq6l", activity, Preferences.DEBUG);
mManager = createReactInstanceManager(activity);
}
return mInstance;
}
public ReactInstanceManager getManager(Activity activity){
if(mManager==null){
mManager = createReactInstanceManager(activity);
}
return mManager;
}
private static ReactInstanceManager createReactInstanceManager(Activity act) {
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setApplication(act.getApplication())
.setJSMainModuleName("")
.setUseDeveloperSupport(Preferences.DEBUG)
.setInitialLifecycleState(LifecycleState.BEFORE_RESUME);
for (ReactPackage reactPackage : Arrays.<ReactPackage>asList(
new MainReactPackage(),
new SpringBoardPackage(),
codePush.getReactPackage()
)) {
builder.addPackage(reactPackage);
}
String jsBundleFile = codePush.getBundleUrl("main.jsbundle");;
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName("main.jsbundle");
}
return builder.build();
}
}
重写ReactActivity:
将官方的ReactActivity粘出来,重写2个方法,onCreate和onDestroy,其余代码不动。
onCreate方法中使用缓存ReactInstanceManager管理器来获得ReactInstanceManager对象,而不是重新创建。
这里曾尝试继承ReactActivity,而不是重写这个类,但是子类覆盖onCreate方法时候,必须要调用super.onCreate,否则编译会报错,但是super.onCreate方法会重新创建rootview,所以实在是绕不过去了。
这里的CodePush是微软的CodePush包,用于更新资源包的,可以不加注释掉,感兴趣的同学可以了解下。
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();
}
}
RNCacheManager rnCacheManager = RNCacheManager.getInstance(this);
mReactInstanceManager = rnCacheManager.getManager(this);
ReactRootView mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
setContentView(mReactRootView);
}
onDestroy方法中,不能再调用原有的mReactInstanceManager.destroy()方法了,否则rn初始化出来的对象会被销毁,下次就用不了了。可以把它注释掉,试了不会报错,最好的办法就是做个标记,最好一个的时候destroy()。
protected void onDestroy() {
super.onDestroy();
if (mReactInstanceManager != null) {
// mReactInstanceManager.onDestroy();
}
}
亲测,第一次加载时间长点,以后的加载使用 缓存都是秒开的。