• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 分布式数据对象跨设备数据同步
2
3
4## 场景介绍
5
6传统方式下,设备之间的数据同步,需要开发者完成消息处理逻辑,包括:建立通信链接、消息收发处理、错误重试、数据冲突解决等操作,工作量非常大。而且设备越多,调试复杂度也将同步增加。
7
8其实设备之间的状态、消息发送进度、发送的数据等都是“变量”。如果这些变量支持“全局”访问,那么开发者跨设备访问这些变量就能像操作本地变量一样,从而能够自动高效、便捷地实现数据多端同步。
9
10分布式数据对象即实现了对“变量”的“全局”访问。向应用开发者提供内存对象的创建、查询、删除、修改、订阅等基本数据对象的管理能力,同时具备分布式能力。为开发者在分布式应用场景下提供简单易用的JS接口,轻松实现多设备间同应用的数据协同,同时设备间可以监听对象的状态和数据变更。满足超级终端场景下,相同应用多设备间的数据对象协同需求。与传统方式相比,分布式数据对象大大减少了开发者的工作量。
11
12
13## 基本概念
14
15- **分布式内存数据库**
16  分布式内存数据库将数据缓存在内存中,以便应用获得更快的数据存取速度,不会将数据进行持久化。若数据库关闭,则数据不会保留。
17
18- **分布式数据对象**
19  分布式数据对象是一个JS对象型的封装。每一个分布式数据对象实例会创建一个内存数据库中的数据表,每个应用程序创建的内存数据库相互隔离,对分布式数据对象的“读取”或“赋值”会自动映射到对应数据库的get/put操作。
20
21  分布式数据对象的生命周期包括以下状态:
22
23  - 未初始化:未实例化,或已被销毁。
24  - 本地数据对象:已创建对应的数据表,但是还无法进行数据同步。
25  - 分布式数据对象:已创建对应的数据表,设备在线且组网内设置同样sessionId的对象数>=2,可以跨设备同步数据。若设备掉线或将sessionId置为空,分布式数据对象退化为本地数据对象。
26
27
28## 运作机制
29
30**图1** 分布式数据对象运作机制 
31
32![distributedObject](figures/distributedObject.jpg)
33
34分布式数据对象生长在分布式内存数据库之上,在分布式内存数据库上进行了JS对象型的封装,能像操作本地变量一样操作分布式数据对象,数据的跨设备同步由系统自动完成。
35
36
37### JS对象型存储与封装机制
38
39- 为每个分布式数据对象实例创建一个内存数据库,通过SessionId标识,每个应用程序创建的内存数据库相互隔离。
40
41- 在分布式数据对象实例化的时候,(递归)遍历对象所有属性,使用“Object.defineProperty”定义所有属性的set和get方法,set和get中分别对应数据库一条记录的put和get操作,Key对应属性名,Value对应属性值。
42
43- 在开发者对分布式数据对象进行“读取”或者“赋值”的时候,都会自动调用到set和get方法,映射到对应数据库的操作。
44
45**表1** 分布式数据对象和分布式数据库的对应关系
46
47| 分布式对象实例 | 对象实例 | 属性名称 | 属性值 |
48| -------- | -------- | -------- | -------- |
49| 分布式内存数据库 | 一个数据库(sessionID标识) | 一条数据库记录的key | 一条数据库记录的value |
50
51
52### 跨设备同步和数据变更通知机制
53
54分布式数据对象,最重要的功能就是对象之间的数据同步。可信组网内的设备可以在本地创建分布式数据对象,并设置sessionID。不同设备上的分布式数据对象,通过设置相同的sessionID,建立对象之间的同步关系。
55
56如下图所示,设备A和设备B上的“分布式数据对象1”,其sessionID均为session1,这两个对象建立了session1的同步关系。
57
58  **图2** 对象的同步关系  
59
60![distributedObject_sync](figures/distributedObject_sync.jpg)
61
62一个同步关系中,一个设备只能有一个对象加入。比如上图中,设备A的“分布式数据对象1”已经加入了session1的同步关系,所以设备A的“分布式数据对象2”就加入失败了。
63
64建立同步关系后,每个Session有一份共享对象数据。加入了同一个Session的对象,支持以下操作:
65
66(1)读取/修改Session中的数据。
67
68(2)监听数据变更,感知其他设备对共享对象数据的修改。
69
70(3)监听状态变更,感知其他设备的加入和退出。
71
72
73### 同步的最小单位
74
75关于分布式数据对象的数据同步,值得注意的是,同步的最小单位是“属性”。比如,下图中对象1包含三个属性:name、age和parents。当其中一个属性变更时,则数据同步时只需同步此变更的属性。
76
77**图3** 数据同步视图 
78
79
80![distributedObject_syncView](figures/distributedObject_syncView.jpg)
81
82
83### 对象持久化缓存机制
84
85分布式对象主要运行在应用程序的进程空间。当调用分布式对象持久化接口时,通过分布式数据库对对象进行持久化和同步,进程退出后数据也不会丢失。
86
87该场景是分布式对象的扩展场景,主要用于以下情况:
88
89- 在设备上创建持久化对象后APP退出,重新打开APP,创建持久化对象,加入同一个Session,数据可以恢复到APP退出前的数据。
90
91- 在设备A上创建持久化对象并同步后持久化到设备B后,A设备的APP退出,设备B打开APP,创建持久化对象,加入同一个Session,数据可以恢复到A设备退出前的数据。
92
93
94## 约束限制
95
96- 不同设备间只有相同bundleName的应用才能直接同步。
97
98- 分布式数据对象的数据同步发生在同一个应用程序下,且同sessionID之间。
99
100- 不建议创建过多的分布式数据对象,每个分布式数据对象将占用100-150KB内存。
101
102- 每个分布式数据对象大小不超过500KB。
103
104- 设备A修改1KB数据,设备B收到变更通知,50ms内完成。
105
106- 单个应用程序最多只能创建16个分布式数据对象实例。
107
108- 考虑到性能和用户体验,最多不超过3个设备进行数据协同。
109
110- 如对复杂类型的数据进行修改,仅支持修改根属性,暂不支持下级属性修改。
111
112- 支持JS接口间的互通,与其他语言不互通。
113
114
115## 接口说明
116
117以下是分布式对象跨设备数据同步功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见[分布式数据对象](../reference/apis/js-apis-data-distributedobject.md)。
118
119
120
121| 接口名称 | 描述 |
122| -------- | -------- |
123| create(context: Context, source: object): DataObject | 创建并得到一个分布式数据对象实例。 |
124| genSessionId(): string | 创建一个sessionId,可作为分布式数据对象的sessionId。 |
125| setSessionId(sessionId: string, callback: AsyncCallback<void>): void | 设置同步的sessionId,当可信组网中有多个设备时,多个设备间的对象如果设置为同一个sessionId,就能自动同步。 |
126| setSessionId(callback: AsyncCallback<void>): void | 退出所有已加入的session。 |
127| on(type: 'change', callback: (sessionId: string, fields: Array<string>) => void): void | 监听分布式数据对象的数据变更。 |
128| off(type: 'change', callback?: (sessionId: string, fields: Array<string>) => void): void | 取消监听分布式数据对象的数据变更。 |
129| on(type: 'status', callback: (sessionId: string, networkId: string, status: 'online' \| 'offline' ) => void): void | 监听分布式数据对象的上下线。 |
130| off(type: 'status', callback?: (sessionId: string, networkId: string, status: 'online' \|'offline' ) => void): void | 取消监听分布式数据对象的上下线。 |
131| save(deviceId: string, callback: AsyncCallback<SaveSuccessResponse>): void | 保存分布式数据对象。 |
132| revokeSave(callback: AsyncCallback<RevokeSaveSuccessResponse>): void | 撤回保存的分布式数据对象。 |
133
134
135## 开发步骤
136
137以一次分布式数据对象同步为例,说明开发步骤。
138
1391. 导入`@ohos.data.distributedDataObject`模块。
140
141   ```ts
142   import distributedDataObject from '@ohos.data.distributedDataObject';
143   ```
144
1452. 请求权限。
146
147   1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,配置方式请参见[配置文件权限声明](../security/accesstoken-guidelines.md#配置文件权限声明)。
148   2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见[向用户申请授权](../security/accesstoken-guidelines.md#向用户申请授权)。
149
1503. 创建并得到一个分布式数据对象实例。
151
152   Stage模型示例:
153
154   ```ts
155   // 导入模块
156   import distributedDataObject from '@ohos.data.distributedDataObject';
157   import UIAbility from '@ohos.app.ability.UIAbility';
158   import { BusinessError } from '@ohos.base';
159   import window from '@ohos.window';
160
161   class ParentObject {
162     mother: string
163     father: string
164
165     constructor(mother: string, father: string) {
166       this.mother = mother
167       this.father = father
168     }
169   }
170   class SourceObject {
171     name: string | undefined
172     age: number | undefined
173     isVis: boolean | undefined
174     parent: Object | undefined
175
176     constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
177       this.name = name
178       this.age = age
179       this.isVis = isVis
180       this.parent = parent
181     }
182   }
183
184   class EntryAbility extends UIAbility {
185     onWindowStageCreate(windowStage: window.WindowStage) {
186       let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
187       let source: SourceObject = new SourceObject("amy", 18, false, parentSource);
188       let localObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, source);
189     }
190   }
191   ```
192
193   FA模型示例:
194
195   ```ts
196   // 导入模块
197   import distributedDataObject from '@ohos.data.distributedDataObject';
198   import featureAbility from '@ohos.ability.featureAbility';
199   // 获取context
200   let context = featureAbility.getContext();
201   class ParentObject {
202     mother: string
203     father: string
204     constructor(mother: string, father: string) {
205       this.mother = mother
206       this.father = father
207     }
208   }
209   class SourceObject {
210     name: string | undefined
211     age: number | undefined
212     isVis: boolean | undefined
213     parent: ParentObject | undefined
214     constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
215       this.name = name
216       this.age = age
217       this.isVis = isVis
218       this.parent = parent
219     }
220   }
221   let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
222   let source: SourceObject = new SourceObject("amy", 18, false, parentSource);
223   // 创建对象,该对象包含4个属性类型:string、number、boolean和Object
224   let localObject: distributedDataObject.DataObject = distributedDataObject.create(context, source);
225   ```
226
2274. 加入同步组网。同步组网中的数据对象分为发起方和被拉起方。
228
229   ```ts
230   // 设备1加入sessionId
231   let sessionId: string = '123456';
232
233   localObject.setSessionId(sessionId);
234
235   // 和设备1协同的设备2加入同一个session
236
237   // 创建对象,该对象包含4个属性类型:string、number、boolean和Object
238   let remoteSource: SourceObject = new SourceObject(undefined, undefined, undefined, undefined);
239   let remoteObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, remoteSource);
240   // 收到status上线后remoteObject同步数据,即name变成jack,age是18
241   remoteObject.setSessionId(sessionId);
242   ```
243
2445. 监听对象数据变更。可监听对端数据的变更,以callback作为变更回调实例。
245
246   ```ts
247   localObject.on("change", (sessionId: string, fields: Array<string>) => {
248     console.info("change" + sessionId);
249     if (fields != null && fields != undefined) {
250       for (let index: number = 0; index < fields.length; index++) {
251         console.info(`The element ${localObject[fields[index]]} changed.`);
252       }
253     }
254   });
255   ```
256
2576. 修改对象属性,对象属性支持基本类型(数字类型、布尔类型、字符串类型)以及复杂类型(数组、基本类型嵌套等)。
258
259   ```ts
260   localObject["name"] = 'jack1';
261   localObject["age"] = 19;
262   localObject["isVis"] = false;
263   let parentSource1: ParentObject = new ParentObject('jack1 mom', 'jack1 Dad');
264   localObject["parent"] = parentSource1;
265   ```
266
267   > **说明:**
268   >
269   > 针对复杂类型的数据修改,目前仅支持对根属性的修改,暂不支持对下级属性的修改。
270
271
272   ```ts
273   // 支持的修改方式
274   let parentSource1: ParentObject = new ParentObject('mom', 'Dad');
275   localObject["parent"] = parentSource1;
276   // 不支持的修改方式
277   localObject["parent"]["mother"] = 'mom';
278   ```
279
2807. 访问对象。可以通过直接获取的方式访问到分布式数据对象的属性,且该数据为组网内的最新数据。
281
282   ```ts
283   console.info(`name:${localObject['name']}`);
284   ```
285
2868. 删除监听数据变更。可以指定删除监听的数据变更回调;也可以不指定,这将会删除该分布式数据对象的所有数据变更回调。
287
288   ```ts
289   // 删除变更回调
290   localObject.off('change', (sessionId: string, fields: Array<string>) => {
291     console.info("change" + sessionId);
292     if (fields != null && fields != undefined) {
293       for (let index: number = 0; index < fields.length; index++) {
294         console.info("changed !" + fields[index] + " " + localObject[fields[index]]);
295       }
296     }
297   });
298   // 删除所有的变更回调
299   localObject.off('change');
300   ```
301
3029. 监听分布式数据对象的上下线。可以监听对端分布式数据对象的上下线。
303
304   ```ts
305   localObject.on('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => {
306     console.info("status changed " + sessionId + " " + status + " " +  networkId);
307     // 业务处理
308   });
309   ```
310
31110. 保存和撤回已保存的数据对象。
312
313    ```ts
314    // 保存数据对象,如果应用退出后组网内设备需要恢复对象数据时调用
315    localObject.save("local").then((result: distributedDataObject.SaveSuccessResponse) => {
316      console.info(`Succeeded in saving. SessionId:${result.sessionId},version:${result.version},deviceId:${result.deviceId}`);
317    }).catch((err: BusinessError) => {
318      console.error(`Failed to save. Code:${err.code},message:${err.message}`);
319    });
320
321    // 撤回保存的数据对象
322    localObject.revokeSave().then((result: distributedDataObject.RevokeSaveSuccessResponse) => {
323      console.info(`Succeeded in revokeSaving. Session:${result.sessionId}`);
324    }).catch((err: BusinessError) => {
325      console.error(`Failed to revokeSave. Code:${err.code},message:${err.message}`);
326    });
327    ```
328
32911. 删除监听分布式数据对象的上下线。可以指定删除监听的上下线回调;也可以不指定,这将会删除该分布式数据对象的所有上下线回调。
330
331    ```ts
332    // 删除上下线回调
333    localObject.off('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => {
334      console.info("status changed " + sessionId + " " + status + " " + networkId);
335      // 业务处理
336    });
337    // 删除所有的上下线回调
338    localObject.off('status');
339    ```
340
34112. 退出同步组网。分布式数据对象退出组网后,本地的数据变更对端不会同步。
342
343    ```ts
344    localObject.setSessionId(() => {
345      console.info('leave all session.');
346    });
347    ```
348
349## 相关实例
350
351针对分布式数据对象开发,有以下相关实例可供参考:
352
353- [分布式组网认证(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-4.0-Release/code/SuperFeature/DistributedAppDev/DistributedAuthentication)
354
355- [分布式对象(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-4.0-Release/code/SuperFeature/DistributedAppDev/DistributedNote)