58本地版64位包RN页面大概率白屏卡死问题

1.问题描述

本地版支持64位应用包供主流应用商城要求上架,在测试过程中发现 RN 界面大概率出现白屏、卡死。

2. 基础排查

#case
1同城 64位包,切换到县域/找工作,无此问题
2对比同城、本地版 64位包 RN 相关 so,md5 一致
3排查与 soloader 版本无关
4排查与 common bundle 无关
5经测试排除本地版自定义 RN 载体页原因
6ReactNative 框架、JS 业务无明显错误日志

3. 解决了?module 调用太频繁?抓错人了~

排除以上原因,再看下业务 module 调用,使用命令行:

adb logcat | grep ‘react’

(1) 本地版“找工作”页面,触发 native module 调用如下:

进入后调用了几十次 NativeDataModule.fetchHeader || NativeDataModule.fetchAction,并且列表滑动过程中也在一直触发调用

(2) 本地版“部落”,触发 native module 调用如下:

进入后频繁调用 WBInitialParams.obtainInitialParams

(3) 同城->县域“找工作”,触发 native module 调用如下:

同城同样的页面仅有少量的 WBInitialParams.obtainInitialParams 触发调用

最后和 FE 沟通、联调,发现在政府整改需求中,FE 业务侧更改了相关 module 的触发,一进入页面所有业务请求、埋点请求均会触发相关 module 调用。

恢复到之前的调用频次进行 debug 调试,本地版 64位包“找工作”页面连续进入 20 次未出现白屏、卡死现象。而本地版 32位包同样的调用频次,为何不出现白屏、卡死现象(但是也会影响性能,造成卡顿),难道和 arm64 内存占用变大有关?

“64位”的优点与缺点

  1. 尽管64位与硬件可支持的最大内存无关,但便于单一程序使用更大内存。在32位CPU中,单一程序仅有4GB地址空间,减去被操作系统和标准库所占用的部分,只剩1~3GB可用。如果一个32位系统的RAM超过4GB,单一程序很难充分利用全部空间。

  2. 即使对于物理内存较小的系统,更大的地址空间也有帮助。内存映射文件是种有用的结构,在32位系统中,程序不能映射大文件(通常是指超过几百MB的文件),而64位系统的可用地址空间更大,不必有这方面的担心。

不过,增加指针宽度有个严重的缺点:

在所有其他条件都相同的情况下,单一程序在64位CPU系统中更占内存。因为指针本身也需要存储于内存中,在64位系统 上,这个空间增加了一倍。而大多数程序运用指针很频繁,所以额外占用的空间往往不少。这给缓存带来了压力,从而导致性能降低。

RN 0.57.8 中对于 arm64 的少部分处理 (内存相关):

###很遗憾,经过苦战排查,不是这个原因

#case
164 位包中,其他 RN 页面 debug 连接本地模式下都没有问题,所以之前和 FE 联调的结果被推翻
2debug 断点模式,64位包也没有问题
3FE 提供一个简单的倒计时 bundle,release 方式加载也会出现卡死问题

4. 柳暗花明 - 初始化预创建 WubaRN 对象

那就很纳闷了,感觉冷启动越慢反而越没有问题,而 arm64 的执行速度毫无疑问是比 v7a 快的。那冷启动时做了什么呢?

adb logcat *:S ReactNative:V ReactNativeJS:V

输出下 ReactNative 框架日志:

10-29 16:14:24.115 11757 11757 D ReactNative: ReactInstanceManager.ctor()
10-29 16:14:24.116 11757 11757 D ReactNative: ReactInstanceManager.createReactContextInBackground()
10-29 16:14:24.116 11757 11757 D ReactNative: ReactInstanceManager.recreateReactContextInBackgroundInner()
10-29 16:14:24.116 11757 11757 D ReactNative: ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()
10-29 16:14:24.116 11757 11757 D ReactNative: ReactInstanceManager.recreateReactContextInBackground()
10-29 16:14:24.116 11757 11757 D ReactNative: ReactInstanceManager.runCreateReactContextOnNewThread()
10-29 16:14:24.117 11757 14386 D ReactNative: ReactInstanceManager.createReactContext()
10-29 16:14:24.129 11757 11757 D ReactNative: ReactInstanceManager.attachRootViewToInstance()
10-29 16:14:24.130 11757 14386 D ReactNative: Initializing React Xplat Bridge.
10-29 16:14:24.132 11757 14386 D ReactNative: Initializing React Xplat Bridge before initializeBridge
10-29 16:14:24.136 11757 14386 D ReactNative: Initializing React Xplat Bridge after initializeBridge
10-29 16:14:24.136 11757 14386 D ReactNative: CatalystInstanceImpl.runJSBundle()
10-29 16:14:24.137 11757 14388 D ReactNative: ReactInstanceManager.setupReactContext()
10-29 16:14:24.137 11757 14388 D ReactNative: CatalystInstanceImpl.initialize()

这是初始化 RN 环境的流程,回看下 WubaRN SDK,确实有这个逻辑:

初始化 -> 提前创建 WubaRN 对象 -> 打开载体页 -> 复用上一个 WubaRN 对象 -> 提前创建 WubaRN 对象

主要目的是加快 RN 载体页启动速度。大胆猜测下,是不是由于 arm64 环境下,初始化速度变快,第一个预创建的 WubaRN 对象依赖的其他组件还未初始化完成。这个出错的 WubaRN 对象引起了整个 React Native 环境的全局异常。经过测试,去掉初始化时预创建 WubaRN 对象的逻辑后,64位包下 RN 运行正常。

看看 WubaRN 对象初始化中做了什么:

public ReactInstanceManagerBuilder buildReactInstanceManager(String mainModuleName, Context context) {
        ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
                .setApplication((Application) context.getApplicationContext())
                .addPackage(new MainReactPackage()) //RN内置的Package,里面有相机、存储、图片、网络等功能
                .addPackage(new WubaRCTPackage()) // 自定义Package
                .setRedBoxHandler(mExceptionHandler)
                .setUseDeveloperSupport(RNDebugSwitcher.getInstance().isDebug())
                .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
        // 如果是release包,则我们自己处理Exception,防止崩溃
        builder.setNativeModuleCallExceptionHandler(mExceptionHandler);
        if (!TextUtils.isEmpty(mainModuleName)) {
            builder.setJSMainModulePath(mainModuleName);
        } else {
            builder.setJSMainModulePath("index.android");
        }
        List<WubaBaseReactPackage> packages = RNPackageContainer.getInstance().getPackageExport(WubaBaseReactPackage.class);
        for (WubaBaseReactPackage reactPackage : packages) {
            builder.addPackage(reactPackage);
        }
        builder.addPackage(new ReanimatedPackage());
        String coreBundleUri = BundleFileManager.getInstance().prepare(context).getCoreBundleFile().getAbsolutePath();
        WubaRNLogger.i("ReactInstance prepareReactRootViewAndLoad load bundle " + coreBundleUri);
        builder.setJSBundleLoader(JSBundleLoader.createFileLoader(coreBundleUri));
        return builder;
    }

ReactInstanceManager 是 RN 的核心类,管理着 ReactRootView、Native ReactPackage、JSIModulePackage、ViewManagers、ReactContext 等。而 ReactContext 又管理着 JSBridge 相关的逻辑,如 CatalystInstance.

至于 ReactInstanceManager 依赖的哪些库未初始化完成还需要再深度剖析,期待下章。

5.吐槽

RN 涉及 Native 域、JS 域、Bridge 域,框架本身的日志太少(当然它也无法提供完整的调用链日志,因为本身的设计就导致日志注定断开)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值