RNOH 应用启动白屏/黑屏、组件渲染异常、运行期冻屏的排查方法论与定位指南

在 RNOH 开发中,"应用一片白/黑""组件不显示或显示错""滑动一会就卡死"是最常见、也最易误判的问题类型之一。本文档给出从现象到根因、从分类到处置的系统化定位方法。


一、问题概述

1.1 什么是"黑屏、白屏、渲染异常"

RNOH 的渲染链路较长:JS → TurboModule 桥接 → C++ 适配层(SchedulerDelegate / MountingManagerCAPI)→ ArkUI 节点层(arkui/ArkUINode、NativeNodeApi)→ @ohos C-API。任一环节断裂或错配,都会在屏幕上表现为"看不到内容"或"内容不对"。常见表现:

  • 启动白/黑屏:应用启动后始终停留在加载页/空白页,RN UI 永不出现;
  • 渲染异常:UI 出现但组件不显示、属性不生效、事件不回调、布局抖动或文本不可见;
  • 运行期冻屏/黑屏:UI 一度正常,操作后画面冻结、点击无响应或转黑。

1.2 为什么必须先分类再处理

这三类现象的根因分布在链路完全不同的环节,处置方向相反:

  • 把启动白屏(bundle 没加载/so 没打包)当成渲染 Bug 去查组件树,会南辕北辙;
  • 把渲染异常(props 未下发/事件名映射)当成性能问题去优化,会越改越乱;
  • 把冻屏(C++ 死锁)当成 JS 逻辑 Bug 去改业务代码,会错过框架修复。

因此定位流程的第一步是按现象分类、按链路分层,而不是一开始就翻组件源码。

1.3 三类问题的性质

类别定义链路环节是否需修复
启动白/黑屏bundle 未加载、so 未打包、窗口未就绪导致 UI 永不出现JSBundleProvider / so 加载 / RNAbility 窗口✅ 配置或升级
渲染异常UI 出现但 props/事件/布局/文本有误MountingManagerCAPI / ComponentInstance / ArkUI 节点✅ 适配层或业务
运行期冻屏/黑屏UI 一度正常后因死锁/重负载冻结C++ 锁/线程、主线程 I/O✅ 框架修复或异步化

二、排查思路与整体分析

2.1 核心判定流程

定位的关键是回答一个问题:**"看不到"发生在哪个环节——bundle 没进来、组件树没建/建错、还是建好后被锁住?**

flowchart TB
    A["黑屏 / 白屏 / 渲染异常"] --> B{"UI 是否曾出现?"}
    B -->|"从未出现"| C{"bundle 是否加载成功?"}
    C -->|"否: Metro 超时/文件不存在"| D["分类: 启动白屏-bundle 链<br/>→ 查 JSBundleProvider"]
    C -->|"是: 但崩溃/so 缺失"| E{"FaultLog 是否报 so/lib?"}
    E -->|"是: libRNOHApp/libhermes/RNOH_C_API_ARCH"| F["分类: 启动白屏-so/配置<br/>→ 修构建配置"]
    E -->|"否: 窗口未加载 content"| G["分类: 启动白屏-窗口<br/>→ 查 RNAbility.onAppSetup"]
    B -->|"出现过但错/不显示"| H{"是单组件错还是整体?"}
    H -->|"单组件 props/事件/文本"| I["分类: 渲染异常<br/>→ ArkUI Inspector + onPropsChanged"]
    H -->|"布局抖动/死循环"| J["分类: 渲染异常-布局<br/>→ 查 onLayout/Yoga 舍入"]
    B -->|"出现后冻结/转黑"| K{"AppFreeze? 主线程阻塞?"}
    K -->|"是: std::mutex::lock/死锁"| L["分类: 运行期冻屏<br/>→ 查 C++ 锁/线程"]
    K -->|"是: 主线程繁忙/重负载"| M["分类: 运行期冻屏-繁忙<br/>→ 拆分初始化任务"]

2.2 排查总思路:沿渲染链路逐层定位

渲染异常定位的本质是沿链路找到断裂/分叉点。RNOH 的渲染链路为:

JSBundleProvider 加载 bundle → JS 执行 → SchedulerDelegate(willMount/doMount/didMount)
  → MountingManagerCAPI(handleMutation/doMount) → ComponentInstance(onPropsChanged)
  → arkui/ArkUINode + NativeNodeApi(@ohos C-API)

排查应按这条链路自上而下逐层确认,每层都读真实源码:

  1. bundle 是否进来 — JSBundleProvider 链是否拿到 bundle(见 [源码] src/main/ets/RNOH/JSBundleProvider.ts);
  2. 组件树是否建/建对 — MountingManagerCAPI::doMount / handleMutation 是否执行、mutation 是否正确(见 [源码] src/main/cpp/RNOH/MountingManagerCAPI.cpp);
  3. props 是否下发 — ComponentInstance::onPropsChanged 是否收到正确值(见 [源码] RNOHCorePackage/ComponentInstances/*ComponentInstance.cpp);
  4. ArkUI 节点是否生效 — arkui/ArkUINode 调用 @ohos C-API 是否成功;
  5. 是否被锁住 — 冻屏则查 C++ 锁/线程(FontRegistry、TurboModuleProvider、SchedulerDelegate)。

关键原则:先用 ArkUI Inspector / hilog 定界"在哪一层断裂",再深入该层读源码,不要一开始就逐文件翻组件实现。

2.3 黑屏白屏渲染异常常见模式速查

模式分类说明
Metro 超时白屏启动白屏无 Metro 服务时 MetroJSBundleProvider 仍优先尝试 WebSocket 连接
bundle 缓存旧版本白屏启动白屏FileJSBundleProvider 设备缓存优先级高于 rawfile,HAP 更新后加载旧 bundle
so 缺失崩溃黑屏启动黑屏libRNOHApp/libhermes.so 未打进 HAP,libRNOHApp is undefined
CAPI 未配置闪退启动黑屏未设 RNOH_C_API_ARCH 启动即崩
窗口 801 卡加载页启动白屏windowRectChange 设备不支持(801),loadContent 被跳过
props 未下发/枚举漏项渲染异常onPropsChanged 收不到值或枚举转换 abort 兜底
事件名映射错渲染异常emitComponentEvent 用 onStop 而非 stop
Yoga 舍入 onLayout 死循环渲染异常3x 屏浮点舍入震荡,文本反复触发 layout
Text 不渲染渲染异常CAPI 文字管线与设备兼容性问题
死锁冻屏运行期冻屏FontRegistry/TurboModuleProvider/Surface 锁顺序不一致
worker 同步取窗口卡死运行期冻屏SafeAreaInsetsProvider 在 worker 同步调窗口属性

三、分步排查过程(工具使用)

源码查阅:RNOH 源码在本地仓库 packages/tester/harmony/react_native_openharmony/{文件路径};RNOH 文档在线:https://gitcode.com/CPF-RN/ohos_react_native/blob/main/docs/zh-cn/{文件路径}。

3.1 排查准备:定界现象与采集现场

第一步不是读源码,而是把现象钉死到某一类、采集现场:

  1. 判断 UI 是否曾出现:完全空白 → 启动期;出现后错 → 渲染异常;出现后冻结 → 运行期冻屏。
  2. 采集日志:
    • 启动白屏:hilog 抓启动链日志,重点看 JSBundleProvider、RNAbility.onAppSetup、so 加载;
    • 崩溃黑屏:DevEco Studio FaultLog 取 CppCrash/JS Crash;
    • 冻屏:FaultLog 取 AppFreeze(THREAD_BLOCK_6S/APP_INPUT_BLOCK/LIFECYCLE_TIMEOUT),并 hdc file recv /data/log/faultlog/faultlogger/ .(见 [在线源码] docs/zh-cn/03-调测/RNOH应用稳定性问题定位指南.md §3)。
  3. 抓 trace:仅靠卡死栈无法判断主线程"忙"还是"堵",用 DevEco Studio Frame/trace 补充现场(见稳定性定位指南 §3.2)。
  4. 环境自检:确认 RNOH_C_API_ARCH、构建模式(Debug/Release)、Metro 是否在跑、bundle 来源(设备文件 vs rawfile vs 服务器)。

3.2 步骤 1:定位启动白屏——bundle 与 so 链路

目标:确认 bundle 是否加载成功、so 是否齐备、窗口是否加载 content。

bundle 链路(见 [源码] JSBundleProvider.ts):

  • AnyJSBundleProvider 按数组顺序逐个尝试 provider,先成功者胜(getBundle 第 375 行循环);
  • MetroJSBundleProvider 优先级最高时,无 Metro 服务会先尝试 WebSocket 连接,超时白屏;
  • FileJSBundleProvider 从设备路径读取(fs.access(this.path),第 115 行),优先级高于 rawfile,HAP 更新后不清除——是"装了新包还白屏/行为没变"的高频根因;
  • ResourceJSBundleProvider 从 rawfile 加载(getRawFdSync,第 168 行),是 release 包的正常来源。

常见根因与处置(见 [在线源码] docs/zh-cn/03-调测/运行类FAQ.md、定位定界指导类FAQ.md):

现象/日志根因处置
启动闪退,提示未设 RNOH_C_API_ARCHCAPI 版本未声明设 RNOH_C_API_ARCH=1,重启 IDE,Clean 后重编;不生效删 .cxx
Coundn't create bindings between ETS and CPP. libRNOHApp is undefinedlibrnoh_app.so 缺失或 libhermes.so 未打包查 entry/build/.../arm64-v8a/ 是否有 so;补 build-profile.json5 的 externalNativeOptions;确认 libhermes.so 打进 HAP
混合方案闪退 insertChildCustomRNComponentFrameNodeFactory 未正确创建正确创建工厂或升级 RNOH
混合方案 Cannot read property tag of undefined创建 RNApp/RNInstance 未传 arkTsComponentNames把组件 name 放数组作 arkTsComponentNames 传入
cannot find record '&@rnoh/...generated/ts&<版本>'codegen ets 生成到 oh_modules,运行时找不到自 CLI 0.77.23 起用 --ets-output-path 生成到源码目录并调整 import
Metro Websocket connection failed无 Metro 服务仍优先连服务器注释 MetroJSBundleProvider,强制用本地 bundle
装新 HAP 行为不变FileJSBundleProvider 设备缓存未清hdc shell "rm -f /data/storage/el2/base/files/bundle.harmony.js"
断开设备重启后白屏用了 Metro 热加载终止 Metro,按 release bundle 重打包(见 定位定界指导类FAQ.md)

3.3 步骤 2:定位启动白屏——窗口与 content 加载

目标:确认 RNAbility 窗口监听注册与 loadContent 是否被调用。

链路(见 [源码] src/main/ets/RNOH/RNAbility.ets):

  • onWindowStageCreate → onAppSetup(第 208 行):依次执行 unsubscribeWindowListeners → onPrepareRNInstance → getMainWindow → subscribeWindowListeners(第 222 行)→ onWindowSetup → onWindowStageSetup(loadContent,第 225 行);
  • subscribeWindowListeners(第 140 行)注册 windowSizeChange 与 windowRectChange。

关键陷阱:windowRectChange 在部分设备(如 OpenHarmony 开发板)不支持,抛错误码 801(Capability not supported)。若 subscribeWindowListeners 抛 801 且无兜底,loadContent 被跳过 → 应用永远卡在加载页(白屏)。

处置:

  • 0.72.141(ArkTS)需在 RNAbility.ets 为 subscribeWindowListeners 各监听单独 try-catch,并在外层 catch 兜底调用 onWindowStageSetup(见 docs/开发者指南/03-开发调试/OpenHarmony运行RN应用.md §2.5);
  • 0.82.30(CAPI)已在基类内置等效保护,一般无需手动 patch;
  • 开发板固定分辨率,windowRectChange 失败不影响核心功能,可安全兜底。

非致命错误可忽略:Failed to get customDensity、Failed to get maxFontScale {code:1300002}、Turbo Module 'RedBox' not found、RSRenderNodeDrawableAdapter node type 129 not supported 等均不影响渲染(见 OpenHarmony运行RN应用.md §2.8)。

3.4 步骤 3:定位渲染异常——组件树与 props

目标:UI 出现但错,定位是组件没建、props 没下发,还是 ArkUI 节点没生效。

核心工具:ArkUI Inspector(见 [在线源码] 定位定界指导类FAQ.md §常见属性效果排查):

  1. DevEco Studio 底部 ArkUI Inspector,选目标应用,按文本/类型/tag 找组件;
  2. 选中组件看右侧属性,定位异常属性;
  3. 属性值有两个来源——handleMutation(组件树操作)与 Animated NativeDriver(动画驱动)。

源码打点定位(见 [在线源码] 定位定界指导类FAQ.md):

打点位置文件 [源码]判断什么
组件创建/移除顺序src/main/cpp/RNOH/SchedulerDelegateCAPI.h handleMutationmutation 是否按预期执行、创建顺序是否错
动画驱动属性更新src/main/cpp/RNOH/SchedulerDelegateCAPI.cpp synchronouslyUpdateViewOnUIThread哪一步属性更新出错
props 下发RNOHCorePackage/ComponentInstances/*ComponentInstance.cpp onPropsChanged组件是否收到正确 props(用 getTag 关联)

渲染管线源码事实([源码]):

  • SchedulerDelegate 实现 willMount/doMount/didMount/handleMutation/updateView/reportMount(见 src/main/cpp/RNOH/SchedulerDelegate.h);
  • MountingManagerCAPI::doMount 带 TraceSection "#RNOH::MountingManager::doMount"(MountingManagerCAPI.cpp 第 62 行),handleMutation 处理 Create/Update/Remove(第 208 行,removeChild 在第 278 行);
  • ArkUI 节点层在 src/main/cpp/RNOH/arkui/(ArkUINode.cpp、NativeNodeApi.cpp、NodeApi.cpp、TextInputNode.cpp、SpanNode.cpp 等),调用 @ohos C-API。

常见根因:

现象根因处置
props 不生效枚举转换对合法值 abort()/返回 -1 兜底被静默处理查 conversions.h 转换函数,补全合法值映射
Fabric 组件事件不回调emitComponentEvent 用 onStop,而 GeneratedEventEmitRequestHandler 的 supportedEventNames 是 stop(JSI 层自动加 on)改用 stop 作为事件名(见 定位定界指导类FAQ.md)
图片加载不出本地图片路径应为 rawfile/assets,沙箱图片不需 assets 层;或 copyAssets 未执行在 ImageComponentInstance.cpp onPropsChanged 打印路径与 assets 比对
文本不可见(0.82.30 CAPI)CAPI 文字管线与设备兼容性问题,非应用层可修用 0.72.141(ArkTS 路径)或 <TextInput editable={false}> 替代 <Text>(见 OpenHarmony运行RN应用.md §2.7)
整体性能极差/白屏用了 Metro 热加载/dev 打包/Debug 构建改 release 构建模式(见 定位定界指导类FAQ.md §整体性能)

3.5 步骤 4:定位渲染异常——布局抖动与文本

目标:布局反复触发、文本测量偏差等问题。

典型根因(见 docs/zh-cn/RNOH与RN社区共性问题清单.md):

  • onLayout 无限循环:Yoga YGRoundValueToPixelGrid 浮点舍入震荡,3x 屏(pointScaleFactor=3)高发;父节点 onLayout 监听文本宽度并刷新 left 坐标,每次舍入结果不同 → 死循环。处置:用 right 替换 left,或 left/width 取整(PixelRatio.roundToNearestPixel())。
  • Text 字体缩放间距异常:allowFontScaling 极值档位下测量与渲染字号换算不一致。处置:关 allowFontScaling 或用 maxFontSizeMultiplier 限制。
  • TextInput transparent 文字仍可见:上游 UndefinedColor 从 INT32_MAX 改为 0,SharedColor::operator bool() 对 transparent 返回 false 被当未设置。处置:用 color: '#ffffff00' 代替 transparent。

上述均为 RN 上游共性问题(iOS/Android/鸿蒙一致),非 RNOH 独有 Bug,按社区方案规避即可。

3.6 步骤 5:定位运行期冻屏——主线程阻塞

目标:UI 一度正常后冻结,判断主线程"忙"还是"堵"。

判定方法(见 [在线源码] RNOH应用稳定性问题定位指南.md §3):

特征主线程繁忙主线程阻塞
3s 与 6s 堆栈不完全一致高度一致,停在固定位置
线程状态RunningSleeping/Waiting
常见调用渲染/布局/组件创建/JS 执行std::mutex::lock/I/O/同步等待

死锁判定要点:

  • 堆栈出现 std::mutex::lock 不一定是死锁,也可能是锁被后台耗时任务长期持有;
  • 对比 3s/6s 堆栈是否停在同一位置,查其他线程是否持有同一把锁或等待主线程;
  • 画线程间锁持有/释放时序,确认是否存在互相等待。

历史冻屏根因速查(见 [在线源码] docs/zh-cn/05-运维/稳定性历史修复/):

冻屏版本根因修复
TimingTurboModule 与 NativeAnimated 死锁0.72.27同步调用链相互等待拆解模块间同步调用
Surface 死锁0.72.27多 Surface 启停时 RNInstance/NapiBridge/SurfaceHandle 同步顺序不一致统一 surface 生命周期同步顺序
getTurboModule 持锁创建 TM 死锁0.77.18持 m_cacheMtx 时同步执行 m_createTurboModule 致锁重入拆成"先读缓存命中即返,未命中释放锁再创建"
主线程销毁 RNInstance 死锁0.77.18/0.72.86主线程持最后引用触发销毁,TurboModuleProvider 重新锁实例引入 SafeWeak/m_isAboutToBeDestroyed 标记
FontRegistry 锁顺序死锁0.82.3/0.77.44JS 线程与 UI 线程对两把锁加锁顺序相反改 std::scoped_lock 原子加锁(见 [源码] FontRegistry.cpp 第 77/94 行)
reportMount 锁重入0.82.17/0.77.59reportMount 在持锁上下文重入 ShadowTreeRegistry 锁切主线程任务队列异步执行
SafeAreaInsetsProvider worker 卡死0.72.112worker 线程同步调 getWindowProperties 切 UI 线程同步等待从 worker TM context 移除该 provider

符号化:C++ 堆栈需用 llvm-addr2line -e <unstripped.so> <addr> 反解;必须确认 BuildID 一致、用 unstripped.so,否则反解到错误行(见稳定性定位指南 §5.4)。

3.7 工具速查

工具用途示例
ArkUI Inspector查组件树与属性定位 props 不生效的组件
DevEco FaultLog / hilog崩溃/冻屏/启动日志取 CppCrash/AppFreeze,看 so 加载、bundle 链
DevEco Frame/trace主线程耗时区间区分冻屏"忙"还是"堵"
llvm-addr2lineC++ 堆栈符号化反解 librnoh_core.so 地址到 FontRegistry::addFontData
hdc shell rm清 bundle/缓存清 FileJSBundleProvider 设备缓存
React DevToolsJS prop 下发与组件树确认 prop 实际传入值
AI 辅助定位堆栈 + 版本 + 设备 → 根因rnoh-render-analyzer(触发词:白屏、黑屏、渲染异常、冻屏)

四、根因定位

4.1 定位方法:找到链路断裂点

根因定位的本质是沿渲染链路逐层确认,直到找到断裂/分叉点:

  1. 从 2.1 流程定界到某一类(启动/渲染/冻屏);
  2. 在该类对应的链路层打点/读源码,定位断裂点;
  3. 用 4.2 模式表识别类型,用 4.3 五问模板固化结论。

示例点拨:启动白屏,hilog 显示 can't find library libhermes.so,且 FileJSBundleProvider 缓存被优先加载 → 断裂点在 so 打包与 bundle 缓存两处。根因是 libhermes.so 未打进 HAP + 设备残留旧 bundle。处置:确认 nativeLib.excludeFromHar 配置 + hdc shell rm 清缓存。

4.2 分叉模式识别指南

对照 2.3 速查表,每种给出"如何识别"与"在哪找":

模式如何识别在哪找
Metro 超时白屏日志有 Websocket connection failedJSBundleProvider.ts MetroJSBundleProvider
bundle 缓存旧版装新 HAP 行为不变,日志显示加载设备文件FileJSBundleProvider.getBundle + hdc shell 查缓存文件
so 缺失黑屏libRNOHApp is undefined / can't find library libhermes.soentry/build/.../arm64-v8a/ 与 HAP libs/
窗口 801 卡加载Failed to setup window {code:801} + 卡加载页RNAbility.ets subscribeWindowListeners
props 未下发Inspector 组件存在但属性缺/错*ComponentInstance.cpp onPropsChanged + conversions.h
事件名映射错emitComponentEvent 不回调RNOHGeneratedPackage.h supportedEventNames
布局死循环onLayout 高频触发、文本反复刷新Yoga YGRoundValueToPixelGrid(3x 屏高发)
死锁冻屏AppFreeze + 3s/6s 堆栈停 mutex::lock 同一位置FontRegistry/TurboModuleProvider/SchedulerDelegate

4.3 结论五问模板

每次定位完,用下表固化结论(作为待填模板,而非预填答案):

问题含义
What现象是什么(白屏/黑屏/组件错/冻屏,JS 侧可观察)
Where链路何处断裂(指明文件 + 链路层,源码或在线源码)
Why为什么断裂(bundle/so/props/锁/布局)
分类启动白/黑屏 / 渲染异常 / 运行期冻屏
分类依据为什么归入此类(引用日志/堆栈/Inspector 证据)

五、解决方案

5.1 何时提出修复

仅在以下情况提出修复:

  1. 命中已修复的历史冻屏/崩溃且当前版本低于修复版本 → 升级(见《历史版本问题定位指南》);
  2. 是适配层缺口(props 枚举漏项、事件名映射、so 未打包) → 补适配或修配置;
  3. 是业务侧布局问题(onLayout 死循环、字体缩放) → 按社区方案规避。

以下情况不要提出修复:现象是上游共性问题(按社区方案规避即可);现象由 Metro 热加载/dev 构建/Debug 模式导致(改 release 构建即可);开发板设备能力限制(如 801,兜底处理即可)。

5.2 修复策略

适用时按以下策略之一落地。任何方案都必须指明文件、改动思路与风险评估,并配套测试。

策略适用场景方法
修正 bundle/so 配置启动白/黑屏注释无用的 MetroJSBundleProvider;补 externalNativeOptions 与 nativeLib.excludeFromHar;确认 libhermes.so 打进 HAP;清 FileJSBundleProvider 缓存
窗口监听兜底801 卡加载页RNAbility.ets 为 subscribeWindowListeners 各监听单独 try-catch,外层 catch 兜底 onWindowStageSetup
补全 props/事件映射渲染异常conversions.h 补全枚举映射;emitComponentEvent 用去 on 前缀的事件名;ImageComponentInstance 对齐路径
修正布局布局死循环/文本异常left→right 或取整;关 allowFontScaling;transparent→#ffffff00
异步化/拆任务主线程繁忙冻屏复杂计算移出主线程;拆页面初始化;同步 I/O 改异步
升级修复死锁死锁冻屏升级到含 scoped_lock/SafeWeak/异步 reportMount 修复的版本

风险评估要点:窗口兜底改动需回归正常 Phone/Tablet 设备的窗口行为;props 映射补全需对照 ArkUI 枚举选语义最近项;异步化需确认回调时序不破坏 RN 契约。

5.3 仅文档输出(现象为设备限制/上游共性时)

当分类为"设备能力限制"(如开发板 801)或"上游共性问题"时,输出应:清晰记录现象及成因;解释设备/平台机制;说明开发者侧影响;给出规避模式(如"用 right 代替 left""用 #ffffff00 代替 transparent""开发板固定分辨率可忽略 801")。


六、经验总结

6.1 六条核心教训

  1. 先分"从未出现"还是"出现后错" — 完全空白走启动链路(bundle/so/窗口),出现后错走渲染链路(props/事件/布局),混不得。
  2. 白屏先查 bundle 缓存与 so 打包 — FileJSBundleProvider 设备缓存优先级最高且 HAP 更新不清;libhermes.so 未打包是 libRNOHApp is undefined 的常见根因。
  3. 渲染异常先用 ArkUI Inspector 定界 — 用 Inspector 看组件是否存在、属性是否对,再决定去 onPropsChanged 还是 conversions.h 打点,不要盲目翻源码。
  4. 事件名要去 on 前缀 — emitComponentEvent 用 stop 不是 onStop,supportedEventNames 存的是去前缀名,JSI 层自动加 on。
  5. 冻屏先判"忙"还是"堵" — 对比 3s/6s 堆栈:一致是阻塞(查锁),不一致是繁忙(拆任务);mutex::lock 不一定是死锁。
  6. 不要跳过符号化与 BuildID 核验 — C++ 堆栈必须用 BuildID 匹配的 unstripped.so 反解,否则反解到错误行;每条引用指向真实读过的源码,未读标 [待确认]。

6.2 排查清单(Checklist)

  • 已判断 UI 是"从未出现"还是"出现后错/冻结"
  • 启动白屏:已查 bundle 链(Metro/文件/rawfile)与 so 打包
  • 已清 FileJSBundleProvider 设备缓存后复现
  • 渲染异常:已用 ArkUI Inspector 定位组件与属性
  • 已在 onPropsChanged/handleMutation/synchronouslyUpdateViewOnUIThread 打点
  • 冻屏:已取 AppFreeze 并对比 3s/6s 堆栈判"忙/堵"
  • C++ 堆栈已用 BuildID 匹配的 unstripped.so 符号化
  • 已确认非 Metro 热加载/dev 打包/Debug 构建导致的性能假象

6.3 常见误区对照

误区正解
"白屏就是 RN 渲染 Bug"先查 bundle 是否加载、so 是否齐备、窗口是否 loadContent
"装了新 HAP 还是白屏,代码没改对"FileJSBundleProvider 缓存优先级高于 rawfile,先 hdc shell rm
"事件不回调是组件没注册"可能是事件名带了 on 前缀,supportedEventNames 存的是 stop
"冻屏 + mutex::lock 一定是死锁"先对比 3s/6s 堆栈,可能只是锁被后台耗时任务持有
"addr2line 反解出来了就是那儿"必须先确认 BuildID 一致 + 用 unstripped.so,否则反解错误
"开发板白屏是 RNOH Bug"可能是 windowRectChange 801 设备能力不支持,兜底即可

附录

附录 A:渲染链路与断裂高发区

下图标注了黑屏白屏渲染异常的断裂高发层:

flowchart TB
    subgraph LOAD["启动加载层(白屏高发区)"]
      L1["JSBundleProvider<br/>⚠️ Metro 超时/File 缓存"]:::warn
      L2["so 加载<br/>⚠️ libRNOHApp/libhermes 缺失"]:::warn
      L3["RNAbility 窗口<br/>⚠️ windowRectChange 801"]:::warn
    end
    subgraph TREE["组件树层(渲染异常高发区)"]
      T1["SchedulerDelegate<br/>willMount/doMount/handleMutation"]:::ok
      T2["MountingManagerCAPI<br/>⚠️ props 枚举漏项/abort"]:::warn
      T3["ComponentInstance<br/>⚠️ onPropsChanged 未收到值"]:::warn
    end
    subgraph NODE["ArkUI 节点层"]
      N1["arkui/ArkUINode + NativeNodeApi<br/>@ohos C-API"]:::ok
      N2["⚠️ Text CAPI 管线兼容性"]:::warn
    end
    subgraph FREEZE["运行期冻屏层(黑屏高发区)"]
      F1["C++ 锁/线程<br/>⚠️ FontRegistry/TurboModule/Surface 死锁"]:::warn
      F2["主线程繁忙<br/>⚠️ 重负载/同步 I/O"]:::warn
    end
    L1 --> L2 --> L3 --> T1 --> T2 --> T3 --> N1 --> N2
    T1 -.冻屏.-> F1
    T1 -.冻屏.-> F2
    classDef warn fill:#fef3c7,stroke:#d97706
    classDef ok fill:#dcfce7,stroke:#16a34a

断裂高发区说明:

层高发模式排查动作
bundle 链Metro 超时/设备缓存旧版查 JSBundleProvider.ts + 清 FileJSBundleProvider 缓存
so 加载libRNOHApp/libhermes.so 缺失查 HAP libs/arm64-v8a + nativeLib 配置
窗口801 卡加载页RNAbility.ets subscribeWindowListeners 兜底
props 解析枚举漏项/abort 兜底conversions.h + onPropsChanged 打点
事件映射事件名带 on 前缀RNOHGeneratedPackage.h supportedEventNames
C++ 锁死锁/锁重入3s/6s 堆栈对比 + 升级含 scoped_lock 修复版本

附录 B:判定流程图(完整版)

flowchart TB
    A["黑屏/白屏/渲染异常"] --> B{"UI 是否曾出现?"}
    B -->|"从未出现"| C{"bundle 加载成功?"}
    C -->|"否"| D["启动白屏-bundle 链<br/>→ Metro/缓存/FileJSBundleProvider"]
    C -->|"是"| E{"FaultLog 报 so/lib?"}
    E -->|"是"| F["启动黑屏-so/配置<br/>→ RNOH_C_API_ARCH/libhermes/nativeLib"]
    E -->|"否"| G["启动白屏-窗口<br/>→ RNAbility 801 兜底"]
    B -->|"出现后错"| H{"单组件还是整体?"}
    H -->|"单组件"| I["渲染异常<br/>→ Inspector + onPropsChanged/事件名"]
    H -->|"整体布局"| J["渲染异常-布局<br/>→ onLayout/Yoga 舍入/字体缩放"]
    B -->|"出现后冻结"| K{"AppFreeze?"}
    K -->|"mutex::lock 一致"| L["运行期冻屏-死锁<br/>→ 升级 scoped_lock/SafeWeak 修复"]
    K -->|"主线程繁忙"| M["运行期冻屏-繁忙<br/>→ 拆任务/异步化 I/O"]
    D --> D1["指明文件 + 清缓存 + 对齐配置"]
    F --> F1["指明 build-profile/nativeLib + 补 so"]
    I --> I1["指明 ComponentInstance + 改事件名/枚举"]
    L --> L1["指明修复版本 + 升级"]

附录 C:代码引用格式约定

  • ArkTS:file/path(如 src/main/ets/RNOH/JSBundleProvider.ts)
  • C++:file/path(如 src/main/cpp/RNOH/MountingManagerCAPI.cpp)
  • 本仓库源码标注 [源码],在线文档标注 [在线源码],未读引用标注 [待确认],绝不编造

附录 D:相关资料

  • 稳定性问题定位指南:[在线源码] docs/zh-cn/03-调测/RNOH应用稳定性问题定位指南.md — 内存泄漏/卡死/崩溃的日志、trace、符号化方法
  • 运行类 FAQ:[在线源码] docs/zh-cn/03-调测/运行类FAQ.md — 启动闪退、libRNOHApp is undefined、codegen record 找不到
  • 定位定界指导类 FAQ:[在线源码] docs/zh-cn/03-调测/定位定界指导类FAQ.md — ArkUI Inspector、属性/图片/事件排查、整体性能
  • 调试调测:[在线源码] docs/zh-cn/03-调测/调试调测.md — Metro 热加载、Dev Menu、Element Inspector、LogBox、Trace、subscribeToRNOHErrors
  • OpenHarmony 运行 RN 应用:OpenHarmony 运行 RN 应用 — 开发板 windowRectChange 801、v0.82.30 Text 不渲染、bundle 缓存
  • RNOH 与 RN 社区共性问题清单:共性问题清单 — Yoga 舍入、onLayout 死循环、字体缩放、TextInput transparent
  • 稳定性历史修复:[在线源码] docs/zh-cn/05-运维/稳定性历史修复/{0.72,0.77,0.82}稳定性修复汇总/02-应用冻屏.md — 各版本线冻屏根因与修复
  • AI 辅助问题定位:[在线源码] docs/zh-cn/05-运维/AI辅助问题定位指导.md — 借助 AI 结合堆栈快速获得根因
  • 配套指南:历史版本问题定位指南 · 三端不一致定位指南
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐