您的位置:首页 > Web前端 > React

reactNative白屏优化

2016-05-05 16:00 447 查看


ReactNative安卓首屏白屏优化


问题描述

公司现有app中部分模块使用reactnative开发,在实施的过程中,rn良好的兼容性,极佳的加载、动画性能,提升了我们的开发、测试效率,提升了用户体验。

但是,在android中,当点击某个rn模块的入口按钮,弹出rn的activity到rn的页面展现出来的过程中,会有很明显的白屏现象,不同的机型不同(cpu好的白屏时间短),大概1s到2s的时间。

注意,只有在真机上才会有此现象,在模拟器上没有此现象完全是秒开。ios上也是秒开,测试的最低版本是ios7,iphone4s。

reactnative版本0.20.0。

jsbundle文件大小717kb。


优化效果

经过了大量的源码阅读,和网上资料查找,最终完美的解决了这个问题,无论什么机型,都可以达到秒开,如图(虽然下图是模拟器的截图,但是真机效果基本一样):





优化过程


时间分布

一般优化速度问题,首先就是要找到时间分布,然后根据二八原则,先优化耗时最长的部分。

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%以上的时间。
ReactRootView mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());


这两行代码就是把jsbundle文件读入到内存中,并进行执行,然后初始化各个对象。


优化思路---内存换时间

在app启动时候,就将mReactRootView初始化出来,并缓存起来,在用的时候直接setContentView(mReactRootView),达到秒开。


步骤1
缓存rootview管理器

缓存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();
}
}


步骤2
重写ReactActivity

将官方的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();
}
}


步骤3
在app启动时候初始化缓存rootview管理器

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/20587485

http://zhuanlan.zhihu.com/magilu/20259704

https://yq.aliyun.com/articles/3208?spm=5176.100239.yqblog1.39.t2g49u&utm_source=tuicool&utm_medium=referral
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: