
离线优先应用:RN + 鸿蒙分布式数据库开发备忘录
引言
在移动互联网时代,“离线可用”已成为用户对应用的核心需求之一。无论是户外探险、网络不稳定场景,还是对数据隐私要求高的行业(如医疗、金融),应用都需在无网络环境下保持核心功能可用,并在网络恢复后同步数据。React Native(RN)凭借跨平台能力,结合鸿蒙(HarmonyOS)的分布式数据库(支持本地存储+云端同步),为开发离线优先应用提供了高效解决方案。本文将以“离线待办事项应用”为例,从技术选型到核心实现,总结一套可落地的开发备忘录。
一、离线优先应用的核心需求
1.1 离线数据存储
应用需在无网络时支持数据的增删改查(CRUD),数据存储需满足:
本地持久化:断网时数据不丢失。
结构化查询:支持复杂查询(如按时间、标签筛选任务)。
1.2 网络恢复同步
网络恢复后,本地数据需与云端(或其他设备)同步,确保数据一致性。同步策略需支持:
增量同步:仅同步变更数据,降低带宽消耗。
冲突解决:处理多端同时修改同一数据的场景(如手机和平板同时编辑任务)。
1.3 跨设备协同
应用需支持手机、平板、车机等多设备访问同一离线数据集,数据需在设备间无缝同步。
二、技术选型:RN + 鸿蒙分布式数据库
2.1 鸿蒙分布式数据库能力
鸿蒙提供了多层级存储方案,满足离线应用的本地存储与同步需求:
存储类型 特点 适用场景
关系型数据库(RDB) 基于SQLite封装,支持SQL查询、事务、索引,适合结构化数据(如任务表、用户表)。 需复杂查询、事务操作的离线数据存储。
轻量级存储(Preferences) 键值对存储,基于文件系统,适合小数据量、高频读写的配置参数(如主题模式、用户偏好)。 非结构化、小数据量的快速读写场景。
分布式数据服务(DDS) 支持跨设备数据同步,数据自动同步至云端或其他设备,解决多端一致性问题。 多设备协同的离线数据同步场景。
2.2 RN与鸿蒙的桥接方案
RN通过@ohos/harmonyos-react-native-bridge库调用鸿蒙原生API,实现对分布式数据库的操作。核心步骤:
安装桥接库:
npm install @ohos/harmonyos-react-native-bridge --save
声明权限:在config.json中添加数据库访问权限:
“module”: {
"requestPermissions": [
“name”: “ohos.permission.DISTRIBUTED_DATASYNC” }
}
三、核心实现:离线待办事项应用
3.1 数据模型设计
以“待办任务”为例,定义数据模型(存储于鸿蒙RDB):
字段名 类型 说明
id STRING 主键(UUID生成)
title STRING 任务标题
description STRING 任务描述(可选)
dueDate NUMBER(毫秒) 截止时间
status STRING 状态(TODO/DONE)
createTime NUMBER(毫秒) 创建时间
updateTime NUMBER(毫秒) 最后更新时间
3.2 本地数据库初始化(RN + 鸿蒙)
通过鸿蒙RDB创建本地数据库,并暴露给RN调用:
3.2.1 鸿蒙原生数据库封装(ETS)
// entry/src/main/ets/database/TaskDatabase.ets
import relationalStore from ‘@ohos.data.relationalStore’
export class TaskDatabase {
private static store: relationalStore.RelationalStore | null = null
private static readonly DB_NAME = ‘TodoDB’
private static readonly TABLE_NAME = ‘Task’
// 初始化数据库
public static async init(): Promise<void> {
if (!this.store) {
this.store = await relationalStore.getRelationalStore({
name: this.DB_NAME,
securityLevel: relationalStore.SecurityLevel.S1
})
// 创建任务表
await this.store.createTable({
name: this.TABLE_NAME,
columns: [
name: ‘id’, type: relationalStore.ColumnType.STRING, isPrimaryKey: true },
name: ‘title’, type: relationalStore.ColumnType.STRING },
name: ‘description’, type: relationalStore.ColumnType.STRING },
name: ‘dueDate’, type: relationalStore.ColumnType.INT },
name: ‘status’, type: relationalStore.ColumnType.STRING },
name: ‘createTime’, type: relationalStore.ColumnType.INT },
name: ‘updateTime’, type: relationalStore.ColumnType.INT }
})
}
// 插入任务
public static async insertTask(task: Task): Promise<string> {
const row = await this.store?.insert(this.TABLE_NAME, task)
return row?.id || ‘’
// 查询所有任务
public static async queryAllTasks(): Promise<Task[]> {
const rows = await this.store?.query(this.TABLE_NAME, [‘*’], {})
return rows || []
// 更新任务状态
public static async updateTaskStatus(id: string, status: string): Promise<number> {
return await this.store?.update(
this.TABLE_NAME,
status, updateTime: Date.now() },
id }
) || 0
}
3.2.2 RN组件调用(JS)
// entry/src/main/ets/pages/TodoListPage.ets(RN桥接鸿蒙)
import React, { useEffect, useState } from ‘react’
import { View, Text, FlatList, Button } from ‘react-native’
import { TaskDatabase } from ‘…/database/TaskDatabase’
import { Task } from ‘…/model/Task’
const TodoListPage = () => {
const [tasks, setTasks] = useState<Task[]>([])
useEffect(() => {
// 初始化数据库并加载任务
const loadTasks = async () => {
await TaskDatabase.init()
const tasks = await TaskDatabase.queryAllTasks()
setTasks(tasks)
loadTasks()
}, [])
// 添加任务
const addTask = async (title: string) => {
const newTask: Task = {
id: Date.now().toString(),
title,
description: ‘’,
dueDate: 0,
status: ‘TODO’,
createTime: Date.now(),
updateTime: Date.now()
await TaskDatabase.insertTask(newTask)
setTasks([...tasks, newTask])
// 切换任务状态
const toggleStatus = async (id: string) => {
const task = tasks.find(t => t.id === id)
if (task) {
const newStatus = task.status === ‘TODO’ ? ‘DONE’ : ‘TODO’
await TaskDatabase.updateTaskStatus(id, newStatus)
setTasks(tasks.map(t => t.id === id ? { …t, status: newStatus } : t))
}
return (
<View style={{ padding: 20 }}>
<FlatList
data={tasks}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View style={{ flexDirection: ‘row’, alignItems: ‘center’, marginVertical: 8 }}>
<Button
title={item.status === ‘TODO’ ? ‘待办’ : ‘已完成’}
onPress={() => toggleStatus(item.id)}
color={item.status === ‘TODO’ ? ‘#ff9800’ : ‘#4caf50’}
/>
<Text style={{ marginLeft: 16 }}>{item.title}</Text>
</View>
)}
/>
<Button title=“添加任务” onPress={() => addTask(‘新任务’)} />
</View>
)
3.3 离线同步策略:鸿蒙分布式数据服务(DDS)
为解决多设备数据同步问题,使用鸿蒙的分布式数据服务(DDS)实现增量同步:
3.3.1 同步配置(鸿蒙侧)
在entry/src/main/resources/base/profile/config.json中声明同步规则:
“module”: {
"requestPermissions": [
“name”: “ohos.permission.DISTRIBUTED_DATASYNC” }
},
“distributedData”: {
“syncRules”: [
“localTable”: “Task”,
"cloudTable": "mpaas_Task",
"syncDirection": "bidirectional", // 双向同步
"syncCondition": {}, // 无过滤条件,同步所有数据
"conflictResolution": "latestVersion" // 以最新版本为准
]
}
3.3.2 RN中触发同步(JS)
// 网络恢复时触发同步
import { NetworkInfo } from ‘@ohos.networkInfo’
const checkNetworkAndSync = async () => {
const networkInfo = new NetworkInfo()
const isOnline = await networkInfo.getNetworkState()
if (isOnline) {
// 调用鸿蒙DDS同步接口
const syncResult = await distributedData.sync({
localTable: ‘Task’,
cloudTable: ‘mpaas_Task’
})
console.info(同步结果:${syncResult.success ? ‘成功’ : ‘失败’})
}
// 监听网络变化
networkInfo.on(‘networkChange’, checkNetworkAndSync)
四、关键技术点与避坑指南
4.1 数据一致性保障
版本控制:在数据模型中添加version字段(通过DDS自动生成),同步时校验版本号,避免脏写。
事务操作:对关键操作(如任务状态变更)使用鸿蒙RDB的事务(transaction方法),确保原子性。
4.2 离线性能优化
索引优化:为高频查询字段(如dueDate、status)创建索引,提升查询速度:
// 鸿蒙侧创建索引
await this.store?.createIndex({
tableName: this.TABLE_NAME,
indexName: ‘idx_dueDate’,
columns: [‘dueDate’]
})
批量操作:避免频繁单条插入/更新,使用batchInsert和batchUpdate批量处理数据。
4.3 冲突解决策略
自动合并:对于非冲突字段(如description),采用“最后更新时间”覆盖;对于冲突字段(如status),提示用户手动选择。
日志记录:记录同步冲突详情(如时间、设备ID),便于后续排查。
五、实战总结与最佳实践
5.1 开发流程总结
需求分析:明确离线场景的核心功能(如任务增删改查)和同步策略(双向/单向)。
数据建模:设计结构化数据表,包含必要字段(如id、createTime、version)。
本地存储封装:通过鸿蒙RDB实现本地CRUD,暴露给RN调用。
网络同步集成:使用鸿蒙DDS实现多设备数据同步,配置同步规则和冲突策略。
测试验证:在无网环境下验证功能可用性,网络恢复后检查数据一致性。
5.2 最佳实践
优先使用鸿蒙原生能力:利用RDB的事务和索引优化离线性能,避免RN桥接带来的额外开销。
动态适配设备类型:通过MediaQuery获取屏幕尺寸,调整UI布局(如平板显示任务详情,手机隐藏次要信息)。
监控与日志:使用鸿蒙的PerformanceMonitor监控数据库操作耗时,记录同步日志以便排查问题。
结语
通过RN与鸿蒙分布式数据库的结合,开发者可高效构建离线优先应用,兼顾跨平台复用与本地数据可靠性。核心在于充分利用鸿蒙的分布式能力(RDB+DDS)解决离线存储与同步问题,同时通过RN的跨平台特性降低开发成本。未来,随着鸿蒙多端协同能力的深化,离线优先应用将在更多场景(如工业物联网、车载系统)中发挥重要作用。
