📅 核心任务
本日重点攻克 OpenHarmony 环境下的网络通信 难关。不再是简单的 UI 堆砌,而是打通 "端-云" 链路,使用 React Native 技术栈(Axios + FlatList)实现一个具备 加载状态、错误兜底、下拉刷新 的完整数据列表,并在鸿蒙真机/模拟器上验证运行。
🚀 一、 破局:鸿蒙网络权限的"隐形墙"
在 Android 开发中我们习惯了 AndroidManifest.xml,而在 OpenHarmony 中,权限管理有着更严格的声明式规范。很多初学者代码写得天衣无缝,请求却永远发不出去,往往就是倒在了这一步。
1.1 权限声明机制
OpenHarmony 的权限管理位于 entry/src/main/module.json5 中。不同于 Android 的粗放,鸿蒙对权限有着明确的等级划分。
- 什么是 module.json5? 它是鸿蒙应用的核心配置文件,类似于 Android 的
AndroidManifest.xml或 iOS 的Info.plist。它定义了应用的标识、组件、权限等关键信息。 - requestPermissions 字段:这是一个数组,用来告诉系统"我需要哪些特权"。没有在这里声明的权限,应用运行时一律无法获取。
关键配置 :
打开 harmony/entry/src/main/module.json5,在 module 节点下新增 requestPermissions:
json5
"module": {
// ... 其他配置
"requestPermissions": [
{
"name": "ohos.permission.INTERNET", // 权限名称:允许访问互联网
"reason": "$string:dependency_reason", // 申请理由:需要在多语言资源文件中定义,如果不涉及上架,暂可随意填写
"usedScene": {
"abilities": [
"EntryAbility" // 适用的 Ability 组件,通常是主入口
],
"when": "inuse" // 授权时机:inuse 表示使用时授权
}
}
]
}
1.2 ⚠️ 技术避坑指南:HTTP 明文流量限制
问题现象 :使用 http:// 接口请求失败,但 https:// 正常。
深度解析 :OpenHarmony 默认策略与 Android 高版本类似,禁止明文 HTTP 流量。这是为了防止中间人攻击,保护用户数据安全。
解决方案:
- 首选:服务端升级 HTTPS(最安全)。现在 Let's Encrypt 等机构提供免费证书,升级 HTTPS 是行业标准。
- 临时方案 :在
module.json5的deviceConfig(API 9 以前) 或相关网络安全配置文件中允许明文流量(不推荐生产环境)。但在 RNOH 当前适配中,底层网络库通常遵循系统默认安全策略,建议直接使用 HTTPS 避免不必要的底层调试。
🛠️ 二、 核心技术栈选型:Axios on Harmony
虽然 RN 提供了 fetch,但在企业级开发中,Axios 依然是首选。
2.1 为什么选择 Axios?
在 RNOH 架构中,JavaScript 端的网络请求最终会通过 RN 的 Bridge/TurboModule 调用到鸿蒙系统的网络能力。Axios 基于 XMLHttpRequest 适配,而 RNOH 完整实现了这一 API。
- 拦截器(Interceptors):就像是快递站的安检员。发货前(请求拦截器)可以统一贴上标签(如 Token),收货时(响应拦截器)可以统一拆箱验货(处理 401 登出、统一错误提示)。
- 自动转换 :Axios 会自动将服务器返回的 JSON 字符串转成 JS 对象,少写一行
response.json()。 - 取消请求:页面关闭时,如果请求还没回来,可以取消它,防止组件已销毁但请求回调还在执行导致的内存泄漏。
2.2 封装实战
不要直接在组件里调用 axios.get。建立 src/utils/request.ts,这样如果以后要换网络库,只需要改这一个文件。
typescript
import axios from 'axios';
import { Alert, Platform } from 'react-native';
const instance = axios.create({
baseURL: 'https://api.example.com', // 替换为实际 API
timeout: 10000, // 超时时间:10秒,网络不好时不会一直卡住
});
// 响应拦截器:统一错误处理
instance.interceptors.response.use(
response => response.data, // 直接返回数据部分,过滤掉 header 等信息
error => {
// 针对鸿蒙平台的特定错误日志
// Platform.OS === 'harmony' 是 RNOH 特有的判断,用于区分鸿蒙平台
if (Platform.OS === 'harmony') {
console.error('[RNOH] Network Error:', error);
}
return Promise.reject(error);
}
);
export default instance;
📊 三、 构建高健壮性数据列表
在鸿蒙设备上渲染长列表,性能是关键。我们使用 FlatList 并结合 TypeScript 进行类型约束。FlatList 是 RN 的高性能列表组件,它只渲染屏幕上可见的元素,看不见的会回收,非常适合长列表。
3.1 列表渲染策略
- 防抖动 :网络请求尽量放在
useEffect中。useEffect就像是组件的"生命周期钩子",空数组[]意味着只在组件挂载时执行一次,避免每次渲染都发请求造成死循环。 - 空状态兜底(Empty State) :网络慢或无数据时,不能白屏,要给用户反馈。
ListEmptyComponent属性就是专门干这个的。 - Key 优化 :
keyExtractor必须唯一。React 需要用 key 来识别哪些元素改变了。如果 key 不唯一,Diff 算法就会混乱,导致鸿蒙 ArkUI 渲染性能下降,甚至 UI 错乱。
3.2 完整代码实现 (ProductList.tsx)
tsx
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, ActivityIndicator, StyleSheet, RefreshControl } from 'react-native';
import request from '../utils/request';
// 定义数据结构接口,TS 的好处是写代码时就有提示
interface ItemData {
id: string;
title: string;
desc: string;
}
const ProductList = () => {
// useState: 管理组件内部状态
const [data, setData] = useState<ItemData[]>([]); // 列表数据
const [loading, setLoading] = useState(true); // 是否正在初次加载
const [refreshing, setRefreshing] = useState(false); // 是否正在下拉刷新
const fetchData = async () => {
try {
// 真实请求示例:
// const res = await request.get('/products');
// setData(res.list);
// Mock 数据用于演示:模拟 1秒后返回数据
setTimeout(() => {
// Array.from 生成 20 条模拟数据
setData(Array.from({ length: 20 }).map((_, i) => ({
id: String(i),
title: `鸿蒙跨端商品 ${i + 1}`,
desc: 'React Native for OpenHarmony 运行验证'
})));
setLoading(false); // 停止 loading 转圈
setRefreshing(false); // 停止下拉刷新转圈
}, 1000);
} catch (err) {
console.error(err);
setLoading(false);
setRefreshing(false);
// 实际项目中这里应该提示用户"网络错误"
}
};
// 组件挂载完成后立即请求数据
useEffect(() => {
fetchData();
}, []);
// 下拉刷新触发的方法
const onRefresh = () => {
setRefreshing(true);
fetchData();
};
// 首次加载时的 Loading 页面
if (loading && !refreshing) {
return <ActivityIndicator size="large" color="#0a59f7" style={styles.center} />;
}
return (
<FlatList
data={data}
keyExtractor={(item) => item.id} // 告诉 RN 每一项的唯一 ID 是什么
refreshControl={
// 下拉刷新组件
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
renderItem={({ item }) => (
// 每一行长什么样
<View style={styles.item}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.desc}>{item.desc}</Text>
</View>
)}
ListEmptyComponent={
// 没数据时显示什么
<Text style={styles.emptyText}>暂无数据,请检查网络连接</Text>
}
/>
);
};
const styles = StyleSheet.create({
center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
item: { padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' },
title: { fontSize: 18, fontWeight: 'bold', color: '#333' },
desc: { marginTop: 8, color: '#666' },
emptyText: { textAlign: 'center', marginTop: 50, color: '#999' }
});
export default ProductList;
🔍 四、 深度排查与验证 (Troubleshooting)
在将代码部署到 OpenHarmony 真机/模拟器时,可能会遇到以下问题:
4.1 编译报错:Could not find "libRNOHApp.so"
现象 :引入新库后,C++ 链接失败。
分析 :React Native for OpenHarmony 包含 C++ 代码(原生模块)。如果你更新了依赖,原来的构建缓存可能和新依赖不匹配。
解决:
- 清理构建 :运行
cd harmony && ./hvigorw.bat clean。这相当于把房子拆了重新盖,清除所有旧的构建产物。 - 同步依赖 :确保
oh-package.json5中的依赖已同步。 - 重新 Sync:在 DevEco Studio 中点击 "Sync Project with Gradle Files" (或者类似的 Sync 按钮)。
4.2 运行时:Network Error 但其他 App 正常
分析:
- 权限未生效 :检查
module.json5修改后是否重新安装 了 HAP?鸿蒙的热重载(Hot Reload)通常只更新 JS 代码,不更新配置文件。修改了module.json5必须重新点击 Run 按钮进行全量安装。 - 时间不对:检查设备时间是否准确。HTTPS 协议需要校验 SSL 证书的有效期,如果设备时间还在 1970 年,证书校验就会失败,导致请求被拒绝。
📝 五、 代码提交与 Git 规范
完成验证后,推送到 AtomGit。Git 是团队协作的基石,规范的提交记录能让队友一眼看懂你干了什么。
bash
# 1. 检查状态:看看修改了哪些文件
git status
# 2. 添加变更:把所有修改放入暂存区
git add .
# 3. 提交代码 (遵循 Commit Message 规范)
# 格式: feat(模块): 描述
# feat 表示新功能,fix 表示修复 bug
git commit -m "feat(network): 集成Axios并实现商品列表页
- 配置 module.json5 网络权限
- 封装 Axios 请求工具类
- 实现 FlatList 下拉刷新与空状态处理"
# 4. 推送到远程 dev 分支
git push origin dev
🌟 总结
今天我们跨越了 RNOH 开发的一个重要里程碑:与外部世界通信 。虽然代码层面主要是 JS,但底层的权限配置、网络策略、以及在鸿蒙设备上的实际表现,都体现了跨平台开发的特殊性------既要懂前端的灵动,也要懂原生的规矩。
欢迎加入开源鸿蒙跨平台社区: