在 HarmonyOS 上使用 React Native(RNOH)开发时,AppState 是处理前后台切换的关键 API。推送通知弹窗时机、视频播放器暂停恢复、数据同步刷新等场景都依赖它。然而,鸿蒙下的行为与 iOS 有显著差异,最典型的是 inactive 状态彻底缺失。本文结合真实踩坑经验,梳理 AppState 在鸿蒙上的正确用法与适配要点,所有代码已在 HarmonyOS 6.0 + RNOH 0.84.1(基于 React Native 0.84)上验证通过。
## 一、基础用法:读取当前状态
AppState.currentState 返回当前应用状态,可能的值有 'active'(前台运行)、'background'(后台运行)、'inactive'(仅 iOS,鸿蒙不会出现)和 null(启动瞬间)。
- import { AppState } from 'react-native';
- const currentState = AppState.currentState;
- // 鸿蒙上只会得到 'active' 或 'background' 或 null
复制代码
## 二、监听状态变化
使用 addEventListener 监听 change 事件,在组件卸载时必须调用 subscription.remove() 避免内存泄漏。
- import React, { useEffect, useRef, useState } from 'react';
- import { AppState } from 'react-native';
- const AppStateMonitor = () => {
- const appState = useRef(AppState.currentState);
- const [appStateVisible, setAppStateVisible] = useState(appState.current);
- useEffect(() => {
- const subscription = AppState.addEventListener('change', (nextAppState) => {
- // 检测是否回到前台:注意不要依赖 inactive 做过渡判断
- if (appState.current === 'background' && nextAppState === 'active') {
- console.log('App has come to the foreground!');
- }
- appState.current = nextAppState;
- setAppStateVisible(appState.current);
- });
- return () => {
- subscription.remove();
- };
- }, []);
- return <Text>Current state is: {appStateVisible}</Text>;
- };
复制代码
关键提醒:鸿蒙上 inactive 不会触发,因此状态机逻辑里不能出现 active → inactive → background 这种 iOS 式链条。直接从 active 跳到 background 或反之。
## 三、实际应用场景与鸿蒙适配
### 场景 1:推送通知处理
前台收到推送时用 Modal 或 Toast 展示,后台则交给原生系统处理。
- useEffect(() => {
- const subscription = AppState.addEventListener('change', (state) => {
- if (state === 'active') {
- showInAppNotification(pendingNotification);
- } else if (state === 'background') {
- // 无需额外逻辑,原生系统会处理通知栏展示
- }
- });
- return () => subscription.remove();
- }, []);
复制代码
### 场景 2:视频播放器
切后台时暂停视频,回前台后根据用户之前的状态决定是否恢复。
- const VideoPlayer = () => {
- const videoRef = useRef<Video>(null);
- useEffect(() => {
- const subscription = AppState.addEventListener('change', (state) => {
- if (state === 'background') {
- videoRef.current?.pause();
- } else if (state === 'active') {
- // 如果之前是播放状态,可调用 resume()
- }
- });
- return () => subscription.remove();
- }, []);
- return <Video ref={videoRef} />;
- };
复制代码
### 场景 3:数据同步
回到前台时刷新最新数据,切后台时保存用户草稿。
- const DataSync = () => {
- useEffect(() => {
- const subscription = AppState.addEventListener('change', (state) => {
- if (state === 'active') {
- refreshData();
- } else if (state === 'background') {
- saveDraft();
- }
- });
- return () => subscription.remove();
- }, []);
- return null;
- };
复制代码
## 四、内存警告事件(仅 Android/RNOpenHarmony 适配需注意)
memoryWarning 事件在内存紧张时触发,鸿蒙上目前未实现该事件,但如果你的应用需要兼容 Android 设备,仍可保留监听。
- useEffect(() => {
- // 该事件仅在 Android 上触发,鸿蒙不会触发
- const subscription = AppState.addEventListener('memoryWarning', () => {
- ImageCache.clear();
- clearUnusedData();
- });
- return () => subscription.remove();
- }, []);
复制代码
## 五、踩坑总结与鸿蒙专属建议
1. **inactive 状态鸿蒙不支持**:iOS 专有状态,鸿蒙上 change 事件绝不会给到 'inactive'。如果之前有 state = 'inactive' 的判断逻辑,一定要替换为直接比较 'active' 与 'background'。
2. **初始化时 currentState 可能为 null**:应用启动瞬间,值尚未确定。用 useRef 初始化时建议加一个保护判断。
3. **必须移除监听器**:组件卸载时忘记 subscription.remove() 会导致回调残留,引发奇怪的 bug。
4. **多个组件同时监听不会冲突**:每个监听都是独立的,可放心在多个组件中使用。
5. **模拟器上切后台行为与真机不一致**:务必使用鸿蒙真机测试后台切换、接电话、分屏等场景。
## 六、给开发者的最佳实践
- 使用 useRef 保存 appState 而不是 useState,避免每次状态变化都触发不必要的渲染。
- 只依赖 'active' 和 'background' 两个状态,所有业务逻辑都围绕这两个值展开。
- 切后台时立即暂停占用资源的行为(视频播放、定时器、高频网络请求),回前台时按需恢复。
- 用真机验证边缘场景:锁屏、接听语音电话、进入最近任务列表、分屏前后台切换等。
以上经验基于 RNOH 0.84.1 + HarmonyOS 6.0 实测,不同版本可能存在差异,建议以实际测试结果为准。如果你遇到新坑,欢迎交流,鸿蒙 RN 生态仍在快速迭代中。 |