• ReactNative安卓首屏白屏优化


    前言

    公司现有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页面。

    解决方案

    优化思路:内存换取时间, 在app启动时候,就将mReactRootView初始化出来,并缓存起来,在用的时候直接setContentView(mReactRootView),达到秒开,但是由于每次页面都是继承ReactActivity,页面不同,每次都要重新初始化新的页面,我们把ReactInstanceManager写成单例的模式,创建一次,加载jsbundle文件缓存在内存中,不需要每次都去加载资源,减少加载时间。

    创建RNCacheManager:

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


    亲测,第一次加载时间长点,以后的加载使用 缓存都是秒开的。

  • 相关阅读:
    【第95题】JAVA高级技术-网络编程14(简易聊天室9:使用Socket传递音频)
    [学习笔记]解决因C#8.0的语言特性导致EFCore实体类型映射的错误
    Netty源码阅读(2)之——服务端源码梗概
    在2023年使用Unity2021从Built-in升级到Urp可行么
    AWS Lambda 操作 RDS 示例
    【LLM之RAG】KG_RAG论文阅读笔记
    CDH 09Cloudera Manager Kerberos安装配置(markdown新版二)
    实验: GVRP 配置,三层交换机通讯 ,VLAN 间路由,单臂路由与路由器子接口的配置,vlan-单臂路由技术
    2022短视频神器历时6个月辛苦全力打造的软件短视频运用工具
    BAT面经总结,阿里巴巴、百度、京东、饿了么offer拿到手软!
  • 原文地址:https://blog.csdn.net/weixin_72426331/article/details/127100008