博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ReactNative安卓首屏白屏优化
阅读量:6943 次
发布时间:2019-06-27

本文共 8571 字,大约阅读时间需要 28 分钟。

原文地址:

问题描述

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

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

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

reactnative版本0.20.0。

jsbundle文件大小717kb。

优化效果

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

clipboard.png

优化过程

时间分布

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

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
getPackages() { return Arrays.asList( new MainReactPackage(), new BBReactPackage() ); }}

结语

希望本篇文档能帮助遇到类似问题的小伙伴们。

reactnative虽然不是银弹,但是在目前移动端浏览器兼容性弱爆了的情况下,还是能极大的提升开发测试效率的,性能也是极佳的,看好rn的未来。

参考文章

转载地址:http://ywonl.baihongyu.com/

你可能感兴趣的文章
令牌桶算法
查看>>
P1726 上白泽慧音
查看>>
PHP多进程开发与Redis结合实践
查看>>
丘比特爱情之箭
查看>>
【自学Linux】Linux用户、组、权限(一)
查看>>
排序负数排在左边非负数排在右边
查看>>
linux删除之前的文件日志
查看>>
RPM包制作
查看>>
xen虚拟机管理命令
查看>>
ORACLE体系结构
查看>>
趣学Python之弹球游戏第五阶段--加个球拍
查看>>
【Android】Uri、UriMatcher、ContentUris详解 .
查看>>
oracle 数据库体系结构图解
查看>>
地址记录本
查看>>
安装Linux系统与常见命令(一)
查看>>
辅助DC降级后无法登陆故障排除
查看>>
点点滴滴累计linux命令
查看>>
公钥密码学中的素数以及对称加密
查看>>
Android上的Open×××-TAP模式/策略路由
查看>>
阿里云服务器安装php+nginx+mysql环境
查看>>