有没有遇到过这种画面:用户刚把 OpenHarmony 设备的系统切到深色模式,桌面、设置、系统应用全都变暗了,结果一点进你基于 React Native 开发的应用,整个页面瞬间亮得像个探照灯。用户嘴上不说,心里已经把应用归到了“没品味”那一类——而实际上,你的配色方案用的还是默认白底。
深色模式适配这件事,在 Android 和 iOS 上已经被聊烂了,但在 OpenHarmony + React Native 这个组合下,还真不是简单抄一套 useColorScheme 就能完事的。系统层的深浅色资源、RN 桥接层的配置同步、JS 侧的动态取色、原生容器的窗口背景,四个环节只要断了一个,页面就可能在切换的一瞬间变得不对劲。
这篇就基于我实际在 OpenHarmony 设备上给 RN 应用做深色适配的经历,把容易翻车的点和能直接落地的做法从头到尾捋一遍。适合正在做 OpenHarmony 应用迁移、或者打算在 rk3568 / rk3588 这类开发板上跑 RN 应用的开发者参考。
1. 切了深色模式页面却没反应:先把出问题的链路摸清楚
1.1 从系统切换键到 RN 页面重新渲染,中间到底发生了什么
很多人以为深色模式就是系统给应用发一个“变暗”的信号,应用收到信号后换个背景色就完事了。但实际上,在 OpenHarmony 设备上,深浅色切换是一个从系统配置逐层传递的过程。
OpenHarmony 系统的配置管理服务会维护一个全局 configuration,里面包含颜色模式、字体缩放、语言区域等信息。当用户在设置里把显示模式从浅色切成深色,系统会通知当前前台应用所在的 UIAbility 生命周期回调,同时原生侧的资源加载框架会根据限定词自动切换资源目录——比如应用资源里放了 base/element/color.json 和 dark/element/color.json,系统就能自动取到深色值。
而这个 UIAbility 承载的界面如果是一个 React Native 页面,情况就复杂了。RN 页面真正的内容绘制并不在原生 ArkUI 组件树里,而是在 RN 自带的渲染引擎里,通过适配层把原生组件映射到 OpenHarmony 的组件体系上。所以系统配置更新通知到达 UIAbility 后,还要由 RN 的适配层把这次配置变化转换成 RN 框架里的 Appearance 变更事件,再通过 JS Bridge 通知到 JS 执行上下文。JS 侧调用 useColorScheme 监听到变化,触发重新渲染,页面才会真正变过去。
这条链路里有任何一个环节断掉,用户看到的就是“系统都变暗了,应用还亮堂堂”的尴尬结果。
1.2 实际操作中真正会让适配失败的几个环节
我在适配过程中排查过一类很典型的 case:系统深浅色切换后,日志里能明显看到原生容器收到了配置更新,但 RN 页面纹丝不动。最后定位下来是适配层没有把系统的颜色模式字段同步给 RN 框架的 AppearanceManager,JS 侧拿到的永远是 light。这类问题属于桥接层的支持度问题,换版本或者打补丁才能解决。
另一类 case 则怪自己:JS 侧 useColorScheme 工作正常,深色模式也监听到了,但页面上大量背景色是直接写死在 StyleSheet.create 里的十六进制常量,压根没有参与主题切换。这种属于应用层代码的组织问题,代码写得越多,返工成本越高。
还有一类隐藏比较深的坑在原生壳层。RN 应用在 OpenHarmony 上跑,入口界面通常还是 Stage 模型里的一个 UIAbility 或 Window,RN 页面加载完成之前,窗口里显示的其实是原生容器的背景。如果这个原生容器的窗口背景被硬编码成了纯白,那么即便 JS 侧适配得再好,冷启动、bundle 加载、页面刷新这几个阶段都会出现让人不适的白底闪烁。深色模式一开,这个问题尤其刺眼。
1.3 动手改代码之前,建议先确认的三件事
别急着改 JS 侧的颜色代码,先在目标设备上核实三个基础条件,能帮你避开大量“无用功”。
第一,确认系统版本是否支持深色模式切换。OpenHarmony 的标准系统版本和分支众多,并不是所有设备镜像都在设置里提供深色模式入口。尤其自己编译移植的系统,如果只带了基础组件,设置里找不到深浅色切换也不奇怪。先手动切一次,确认系统级应用能跟着变色,再来谈 RN 适配。
第二,确认 RN 侧能不能感知到系统颜色模式。用一个小 Demo 页面,在 useEffect 里监听 useColorScheme 的变化,把当前值直接 console.log 出来并显示在页面上。如果这个值切换系统深浅色后纹丝不动,说明适配层还没打通,先去解决框架版本的兼容问题;如果能感知到,再深入做 UI 层适配。
第三,找到入口 UIAbi
