HarmonyOS开发跨设备同步实战:多设备协同表模式的联系人同步案例
·
跨设备同步实战:多设备协同表模式的联系人同步案例
分布式是鸿蒙的招牌能力。手机上改了联系人,平板上要看到;平板上加了个备忘录,手机上要同步出现——这就是跨设备数据同步。关系型数据库提供了两种同步模式:多设备协同表和单版本表。下面先用一个"联系人同步"案例把多设备协同表模式讲透,下一篇再讲单版本表。
一、案例背景:多设备联系人同步
一个通讯录应用,用户在手机上添加/修改联系人,平板上要同步看到。需求:
- 手机端新增联系人 → 推送到平板
- 平板端能查询手机同步过来的联系人
- 平板端数据变化时,手机端能收到通知
这正是多设备协同表模式的典型场景。
二、多设备协同表模式的运作机制
先理解机制,再写代码。多设备协同表模式的核心是数据隔离存储:
- 每个设备的数据存在独立的分布式表中,不直接写入本地表
- 分布式表名 = 对端设备 DeviceID + 原表名
- 设备 A 收到设备 B 同步过来的数据,会写入"B 的分布式表",通过
obtainDistributedTableName拿到表名再查 - 不支持修改其他设备同步过来的数据(保障一致性和同步稳定性)
这个"只读对端数据"的限制是设计上的取舍——避免双向修改导致冲突。如果需要双向修改,得用下一篇讲的单版本表模式。
三、第一步:导入模块和申请权限
import { relationalStore } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { UIContext } from '@kit.ArkUI';
const DOMAIN = 0x0000;
跨设备同步需要 ohos.permission.DISTRIBUTED_DATASYNC 权限,要在 module.json5 声明,并在应用首次启动时弹窗向用户申请授权。
四、第二步:建库建表 + 设置分布式表
let store: relationalStore.RdbStore | undefined = undefined;
async function initDistributedDb(context: common.UIAbilityContext) {
const STORE_CONFIG: relationalStore.StoreConfig = {
name: 'RdbTest.db',
securityLevel: relationalStore.SecurityLevel.S3
};
relationalStore.getRdbStore(context, STORE_CONFIG).then(async (rdbStore: relationalStore.RdbStore) => {
store = rdbStore;
// 建联系人表
await store.executeSql(
'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'
);
// 把表设置为分布式表(默认多设备协同表模式)
await store.setDistributedTables(['EMPLOYEE']);
}).catch((err: BusinessError) => {
hilog.error(DOMAIN, 'sync', `获取 RdbStore 失败: ${err.message}`);
});
}
setDistributedTables 是关键——调用后,这张表就具备了跨设备同步能力。默认采用多设备协同表模式。
五、第三步:订阅其他设备的数据变化
平板要实时知道手机改了什么,用订阅:
async function subscribeRemoteChange() {
if (!store) return;
try {
// 查询组网内的设备列表
const deviceManager = distributedDeviceManager.createDeviceManager('com.example.contactSync');
const deviceList = deviceManager.getAvailableDeviceListSync();
const devices: string[] = [];
deviceList.forEach(item => {
if (item.networkId) devices.push(item.networkId);
});
// 订阅远程设备数据变化
store.on('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, async (changedDevices) => {
for (let i = 0; i < changedDevices.length; i++) {
let device = changedDevices[i];
if (!store) return;
hilog.info(DOMAIN, 'sync', `设备 ${device} 数据变化`);
// 获取该设备对应的分布式表名
const distributedTableName = await store.obtainDistributedTableName(device, 'EMPLOYEE');
// 查询该设备同步过来的数据
const predicates = new relationalStore.RdbPredicates(distributedTableName);
const resultSet = await store.query(predicates);
hilog.info(DOMAIN, 'sync', `设备 ${device} 同步了 ${resultSet.rowCount} 条联系人`);
resultSet.close();
}
});
} catch (err) {
hilog.error(DOMAIN, 'sync', `订阅失败: ${(err as BusinessError).message}`);
}
}
关键接口:
on('dataChange', SUBSCRIBE_TYPE_REMOTE, callback):订阅远程设备数据变化obtainDistributedTableName(device, 'EMPLOYEE'):拿到对端设备的分布式表名(拼了 DeviceID)- 用这个表名查谓词,就能读到对端同步过来的数据
六、第四步:推送本地数据到其他设备
手机端新增联系人后,推送到平板:
async function pushContactToRemote() {
if (!store) return;
// 1. 本地插入新联系人
const ret = store.insertSync('EMPLOYEE', {
name: '张三',
age: 28,
salary: 15000
});
hilog.info(DOMAIN, 'sync', `本地插入成功,rowId: ${ret}`);
// 2. 查询组网设备
const deviceManager = distributedDeviceManager.createDeviceManager('com.example.contactSync');
const deviceList = deviceManager.getAvailableDeviceListSync();
const syncTarget: string[] = [];
deviceList.forEach(item => {
if (item.networkId) syncTarget.push(item.networkId);
});
if (syncTarget.length === 0) {
hilog.error(DOMAIN, 'sync', '没有可同步的设备');
return;
}
// 3. 推送数据到其他设备
const predicates = new relationalStore.RdbPredicates('EMPLOYEE');
predicates.inDevices(syncTarget); // 指定目标设备
try {
const result = await store.sync(relationalStore.SyncMode.SYNC_MODE_PUSH, predicates);
hilog.info(DOMAIN, 'sync', '推送完成');
// result 是每个设备的同步结果
for (let i = 0; i < result.length; i++) {
const deviceId = result[i][0];
const syncResult = result[i][1];
if (syncResult === 0) {
hilog.info(DOMAIN, 'sync', `设备 ${deviceId} 同步成功`);
} else {
hilog.error(DOMAIN, 'sync', `设备 ${deviceId} 同步失败,状态: ${syncResult}`);
}
}
} catch (e) {
hilog.error(DOMAIN, 'sync', `推送失败: ${(e as BusinessError).message}`);
}
}
sync 接口两种模式:
SYNC_MODE_PUSH:推送本地变更到其他设备SYNC_MODE_PULL:拉取其他设备的变更到本地
七、第五步:拉取其他设备的数据
async function pullContactFromRemote() {
if (!store) return;
const deviceManager = distributedDeviceManager.createDeviceManager('com.example.contactSync');
const deviceList = deviceManager.getAvailableDeviceListSync();
const syncTarget: string[] = [];
deviceList.forEach(item => {
if (item.networkId) syncTarget.push(item.networkId);
});
const predicates = new relationalStore.RdbPredicates('EMPLOYEE');
predicates.inDevices(syncTarget);
const result = await store.sync(relationalStore.SyncMode.SYNC_MODE_PULL, predicates);
hilog.info(DOMAIN, 'sync', '拉取完成');
}
八、第六步:远程查询(未同步时也能查)
数据还没同步完,或者没触发同步时,想直接查远程设备的数据,用 remoteQuery:
async function remoteQuery() {
if (!store) return;
const deviceManager = distributedDeviceManager.createDeviceManager('com.example.contactSync');
const deviceList = deviceManager.getAvailableDeviceListSync();
const devices: string[] = [];
deviceList.forEach(item => {
if (item.networkId) devices.push(item.networkId);
});
if (devices.length === 0) return;
const predicates = new relationalStore.RdbPredicates('EMPLOYEE');
try {
// 直接查询远程设备上的数据,不需要先同步
const resultSet = await store.remoteQuery(
devices[0], 'EMPLOYEE', predicates, ['ID', 'NAME', 'AGE', 'SALARY']
);
hilog.info(DOMAIN, 'sync', `远程查询结果: ${resultSet.rowCount} 条`);
resultSet.close();
} catch (e) {
hilog.error(DOMAIN, 'sync', `远程查询失败: ${(e as BusinessError).message}`);
}
}
remoteQuery 适合"偶尔看一眼对端数据"的场景,不用维护同步状态。
九、数据同步的访问控制
官方提醒:数据只允许向数据安全标签不高于对端设备安全等级的设备同步。
也就是说,如果数据安全级别是 S3,对端设备安全等级只有 S2,数据不会同步过去。这个机制保证了敏感数据不会流向低安全等级的设备。
设计同步时要注意:
- 别给要同步的数据设太高的安全级别(同步不出去)
- 也别设太低(敏感数据泄露)
十、约束限制速查
| 约束 | 说明 |
|---|---|
| 同时打开数据库数 | 每应用最多 16 个 |
| 订阅回调数 | 单库最多 8 个 |
| 复合键表 | 不能设为分布式表 |
| 端端 vs 端云 | 同一表不能同时配置,且不可切换 |
| 同库同步机制 | 所有分布式表必须同一种机制,不可切换 |
| 多设备协同表 | 不支持修改对端同步过来的数据 |
| 多设备协同表 | 不支持设置 schema |
十一、多设备协同表的适用场景
总结一下这种模式适合什么:
- 单向数据同步:A 设备产生数据,B 设备只读
- 数据展示型场景:联系人、日程、备忘录的"多端可见"
- 不涉及冲突处理:各设备数据隔离,不会互相覆盖
不适合:
- 多端都能改同一份数据:需要单版本表模式
- 需要复杂冲突解决:需要单版本表模式 + schema 配置
十二、总结一下下
- 数据隔离存储:各设备数据在独立分布式表,通过
obtainDistributedTableName查询 - 对端数据只读:不能修改其他设备同步过来的数据
- sync 两种模式:PUSH 推送本地变更,PULL 拉取对端变更
- remoteQuery 直查:不同步也能查远程数据
- 安全级别限制:数据只能流向安全等级足够的设备
- 权限要申请:DISTRIBUTED_DATASYNC 权限 + 用户授权
更多推荐



所有评论(0)