• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 分布式数据对象跨设备数据同步
2
3
4## 场景介绍
5
6传统方式下,设备之间的数据同步,需要开发者完成消息处理逻辑,包括:建立通信链接、消息收发处理、错误重试、数据冲突解决等操作,工作量非常大。而且设备越多,调试复杂度也将同步增加。
7
8其实设备之间的状态、消息发送进度、发送的数据等都是“变量”。如果这些变量支持“全局”访问,那么开发者跨设备访问这些变量就能像操作本地变量一样,从而能够自动高效、便捷地实现数据多端同步。
9
10分布式数据对象即实现了对“变量”的“全局”访问。向应用开发者提供内存对象的创建、查询、删除、修改、订阅等基本数据对象的管理能力,同时具备分布式能力。为开发者在分布式应用场景下提供简单易用的JS接口,轻松实现多设备间同应用的数据协同,同时设备间可以监听对象的状态和数据变更。满足超级终端场景下,相同应用多设备间的数据对象协同需求。与传统方式相比,分布式数据对象大大减少了开发者的工作量。
11
12目前<!--RP2-->分布式数据对象只能在[跨端迁移](../application-models/hop-cross-device-migration.md)和[通过跨设备Call调用实现的多端协同](../application-models/hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)场景中使用。<!--RP2End-->
13
14## 基本概念
15
16- **分布式内存数据库**:分布式内存数据库将数据缓存在内存中,以便应用获得更快的数据存取速度,不会将数据进行持久化。若数据库关闭,则数据不会保留。
17
18- **分布式数据对象**:分布式数据对象是一个JS对象型的封装。每一个分布式数据对象实例会创建一个内存数据库中的数据表,每个应用程序创建的内存数据库相互隔离,对分布式数据对象的“读取”或“赋值”会自动映射到对应数据库的get/put操作。
19
20  分布式数据对象的生命周期包括以下状态:
21
22  - 未初始化:未实例化,或已被销毁。
23  - 本地数据对象:已创建对应的数据表,但是还无法进行数据同步。
24  - 分布式数据对象:已创建对应的数据表,设备在线且组网内设置同样sessionId的对象数&gt;=2,可以跨设备同步数据。若设备掉线或将sessionId置为空,分布式数据对象退化为本地数据对象。
25
26
27## 运作机制
28
29**图1** 分布式数据对象运作机制 
30
31![distributedObject](figures/distributedObject.jpg)
32
33分布式数据对象生长在分布式内存数据库之上,在分布式内存数据库上进行了JS对象型的封装,能像操作本地变量一样操作分布式数据对象,数据的跨设备同步由系统自动完成。
34
35
36### JS对象型存储与封装机制
37
38- 为每个分布式数据对象实例创建一个内存数据库,通过SessionId标识,每个应用程序创建的内存数据库相互隔离。
39
40- 在分布式数据对象实例化的时候,(递归)遍历对象所有属性,使用“Object.defineProperty”定义所有属性的set和get方法,set和get中分别对应数据库一条记录的put和get操作,Key对应属性名,Value对应属性值。
41
42- 在开发者对分布式数据对象进行“读取”或者“赋值”的时候,都会自动调用到set和get方法,映射到对应数据库的操作。
43
44**表1** 分布式数据对象和分布式数据库的对应关系
45
46| 分布式对象实例 | 对象实例 | 属性名称 | 属性值 |
47| -------- | -------- | -------- | -------- |
48| 分布式内存数据库 | 一个数据库(sessionID标识) | 一条数据库记录的key | 一条数据库记录的value |
49
50
51### 跨设备同步和数据变更通知机制
52
53分布式数据对象,最重要的功能就是对象之间的数据同步。可信组网内的设备可以在本地创建分布式数据对象,并设置sessionID。不同设备上的分布式数据对象,通过设置相同的sessionID,建立对象之间的同步关系。
54
55如下图所示,设备A和设备B上的“分布式数据对象1”,其sessionID均为session1,这两个对象建立了session1的同步关系。
56
57  **图2** 对象的同步关系  
58
59![distributedObject_sync](figures/distributedObject_sync.jpg)
60
61一个同步关系中,一个设备只能有一个对象加入。比如上图中,设备A的“分布式数据对象1”已经加入了session1的同步关系,所以设备A的“分布式数据对象2”就加入失败了。
62
63建立同步关系后,每个Session有一份共享对象数据。加入了同一个Session的对象,支持以下操作:
64
65(1)读取/修改Session中的数据。
66
67(2)监听数据变更,感知其他设备对共享对象数据的修改。
68
69(3)监听状态变更,感知其他设备的加入和退出。
70
71分布式数据对象加入session时,如果它的数据与session中的数据不同,则它会更新session中的数据。如果希望分布式数据对象加入sessionId时不更新session中的数据,并且得到session中的数据,需要将对象的属性的值设置为undefined(资产类型的属性则是将它的各个属性值设置为空字符串)。
72
73### 同步的最小单位
74
75关于分布式数据对象的数据同步,值得注意的是,同步的最小单位是“属性”。比如,下图中对象1包含三个属性:name、age和parents。当其中一个属性变更时,则数据同步时只需同步此变更的属性。
76
77对象属性支持基本类型(数字类型、布尔类型、字符串类型)以及复杂类型(数组、基本类型嵌套)。针对复杂类型的数据修改,目前仅支持对根属性的修改,暂不支持对下级属性的修改。
78
79```ts
80dataObject['parents'] = {mom: "amy"}; // 支持的修改
81dataObject['parents']['mon'] = "amy"; // 不支持的修改
82```
83
84**图3** 数据同步视图 
85
86
87![distributedObject_syncView](figures/distributedObject_syncView.jpg)
88
89
90### 对象持久化缓存机制
91
92分布式对象主要运行在应用程序的进程空间。当调用分布式对象持久化接口时,通过分布式数据库对对象进行持久化和同步,进程退出后数据也不会丢失。
93
94该场景是分布式对象的扩展场景,主要用于以下情况:
95
96- 在设备上创建持久化对象后APP退出,重新打开APP,创建持久化对象,加入同一个Session,数据可以恢复到APP退出前的数据。
97
98- 在设备A上创建持久化对象并同步后持久化到设备B后,A设备的APP退出,设备B打开APP,创建持久化对象,加入同一个Session,数据可以恢复到A设备退出前的数据。
99
100### 资产同步机制
101
102在分布式对象中,可以使用[资产类型](../reference/apis-arkdata/js-apis-data-commonType.md#asset)来描述本地实体资产文件,分布式对象跨设备同步时,该文件会和数据一起同步到其他设备上。当前只支持资产类型,不支持[资产类型数组](../reference/apis-arkdata/js-apis-data-commonType.md#assets)。如需同步多个资产,可将每个资产作为分布式对象的一个根属性实现。
103
104## 约束限制
105
106- 目前<!--RP2-->分布式数据对象只能在[跨端迁移](../application-models/hop-cross-device-migration.md)和[通过跨设备Call调用实现的多端协同](../application-models/hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)场景中使用。<!--RP2End-->
107
108- 不同设备间只有相同bundleName的应用才能直接同步。
109
110- 分布式数据对象的数据同步发生在同一个应用程序下,且同sessionID之间。
111
112- 不建议创建过多的分布式数据对象,每个分布式数据对象将占用100-150KB内存。
113
114- 每个分布式数据对象大小不超过500KB。
115
116- 设备A修改1KB数据,设备B收到变更通知,50ms内完成。
117
118- 单个应用程序最多只能创建16个分布式数据对象实例。
119
120- 考虑到性能和用户体验,最多不超过3个设备进行数据协同。
121
122- 如对复杂类型的数据进行修改,仅支持修改根属性,暂不支持下级属性修改。[资产同步机制](#资产同步机制)中,资产类型的数据支持下一级属性修改。
123
124- 支持JS接口间的互通,与其他语言不互通。
125
126## 接口说明
127
128以下是分布式对象跨设备数据同步功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见[分布式数据对象](../reference/apis-arkdata/js-apis-data-distributedobject.md)。
129
130
131
132| 接口名称 | 描述 |
133| -------- | -------- |
134| create(context: Context, source: object): DataObject | 创建并得到一个分布式数据对象实例。 |
135| genSessionId(): string | 创建一个sessionId,可作为分布式数据对象的sessionId。 |
136| setSessionId(sessionId: string, callback: AsyncCallback&lt;void&gt;): void | 设置同步的sessionId,当可信组网中有多个设备时,多个设备间的对象如果设置为同一个sessionId,就能自动同步。 |
137| setSessionId(callback: AsyncCallback&lt;void&gt;): void | 退出所有已加入的session。 |
138| on(type: 'change', callback: (sessionId: string, fields: Array&lt;string&gt;) => void): void | 监听分布式数据对象的数据变更。 |
139| off(type: 'change', callback?: (sessionId: string, fields: Array&lt;string&gt;) => void): void | 取消监听分布式数据对象的数据变更。 |
140| on(type: 'status', callback: (sessionId: string, networkId: string, status: 'online' \| 'offline' ) => void): void | 监听分布式数据对象的上下线。 |
141| off(type: 'status', callback?: (sessionId: string, networkId: string, status: 'online' \|'offline' ) => void): void | 取消监听分布式数据对象的上下线。 |
142| save(deviceId: string, callback: AsyncCallback&lt;SaveSuccessResponse&gt;): void | 保存分布式数据对象。 |
143| revokeSave(callback: AsyncCallback&lt;RevokeSaveSuccessResponse&gt;): void | 撤回保存的分布式数据对象。 |
144| bindAssetStore(assetKey: string, bindInfo: BindInfo, callback: AsyncCallback&lt;void&gt;): void | 绑定融合资产。 |
145
146
147## 开发步骤
148
149### 在跨端迁移中使用分布式数据对象迁移数据
150
1511. 迁移发起端在onContinue接口中创建分布式数据对象并保存数据到接收端:
152
153    1.1 调用create接口创建并得到一个分布式数据对象实例。
154
155    1.2 调用genSessionId接口创建一个sessionId,调用setSessionId接口设置同步的sessionId,并将这个sessionId放入wantParam。
156
157    1.3 从wantParam获取接收端设备networkId,使用这个networkId调用save接口保存数据到接收端。
158
1592. 接收端在onCreate和onNewWant接口中创建分布式数据对象并注册恢复状态监听:
160
161    2.1 调用create接口创建并得到一个分布式数据对象实例。
162
163    2.2 注册恢复状态监听。收到状态为'restored'的回调通知时,表示接收端分布式数据对象已恢复发起端保存过来的数据。
164
165    2.3 从want.parameters中获取发起端放入的sessionId,调用setSessionId接口设置同步的sessionId。
166
167> **说明:**
168>
169> - 跨端迁移时,在迁移发起端调用setsessionId接口设置同步的sessionId后,必须再调用save接口保存数据到接收端。
170>
171<!--RP1-->
172> - 跨端迁移需要配置`continuable`标签,详见[跨端迁移开发步骤](../application-models/hop-cross-device-migration.md#开发步骤)。<!--RP1End-->
173>
174> - wantParam中的"sessionId"字段可能被其他服务占用,建议自定义一个key存取sessionId。
175>
176> - 可以使用资产类型记录资产附件(文件、图片、视频等类型文件)的相关信息,迁移资产类型数据时,对应的资产附件会一起迁移到对端。
177>
178> - 接收端需要将业务数据的初始值设置为undefined,才能恢复发起端保存的数据,否则接收端的数据会覆盖同步到发起端。如果是资产数据,需要将资产数据的各个属性设置为空字符串而不是将整个资产数据设置为undefined。
179>
180> - 暂不支持资产类型数组,如果要迁移多个文件,在业务数据中定义多条资产数据来记录。
181>
182> - 目前仅支持迁移分布式文件目录下的文件,非分布式文件目录下的文件可以复制或移动到分布式文件目录下再进行迁移。文件的操作和URI的获取详见[文件管理](../reference/apis-core-file-kit/js-apis-file-fs.md)和[文件URI](../reference/apis-core-file-kit/js-apis-file-fileuri.md)。
183
184```ts
185import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
186import { hilog } from '@kit.PerformanceAnalysisKit';
187import { window } from '@kit.ArkUI';
188import { commonType, distributedDataObject } from '@kit.ArkData';
189import { fileIo, fileUri } from '@kit.CoreFileKit';
190import { BusinessError } from '@kit.BasicServicesKit';
191
192// 业务数据定义
193class Data {
194  title: string | undefined;
195  text: string | undefined;
196  attachment: commonType.Asset; // 可以使用资产类型记录分布式目录下的文件,迁移资产数据时,对应的文件会一起迁移到接收端。(不迁移文件时不需要此字段,下方代码中的createAttachment、createEmptyAttachment方法也都不需要。)
197  // attachment2: commonType.Asset; // 暂不支持资产类型数组,如果要迁移多个文件,在业务数据中定义多条资产数据来记录
198
199  constructor(title: string | undefined, text: string | undefined, attachment: commonType.Asset) {
200    this.title = title;
201    this.text = text;
202    this.attachment = attachment;
203  }
204}
205
206const TAG = '[DistributedDataObject]';
207let dataObject: distributedDataObject.DataObject;
208
209export default class EntryAbility extends UIAbility {
210  // 1. 迁移发起端在onContinue接口中创建分布式数据对象并保存数据到接收端
211  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult | Promise<AbilityConstant.OnContinueResult> {
212    // 1.1 调用create接口创建并得到一个分布式数据对象实例
213    let attachment = this.createAttachment();
214    let data = new Data('The title', 'The text', attachment);
215    dataObject = distributedDataObject.create(this.context, data);
216
217    // 1.2 调用genSessionId接口创建一个sessionId,调用setSessionId接口设置同步的sessionId,并将这个sessionId放入wantParam
218    let sessionId = distributedDataObject.genSessionId();
219    console.log(TAG + `gen sessionId: ${sessionId}`);
220    dataObject.setSessionId(sessionId);
221    wantParam.distributedSessionId = sessionId;
222
223    // 1.3 从wantParam获取接收端设备networkId,使用这个networkId调用save接口保存数据到接收端
224    let deviceId = wantParam.targetDevice as string;
225    console.log(TAG + `get deviceId: ${deviceId}`);
226    dataObject.save(deviceId);
227    return AbilityConstant.OnContinueResult.AGREE;
228  }
229
230  // 2. 接收端在onCreate和onNewWant接口中创建分布式数据对象并加入组网进行数据恢复
231  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
232    if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
233      if (want.parameters && want.parameters.distributedSessionId) {
234        this.restoreDistributedDataObject(want);
235      }
236    }
237  }
238
239  // 2. 接收端在onCreate和onNewWant接口中创建分布式数据对象并加入组网进行数据恢复
240  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
241    if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
242      if (want.parameters && want.parameters.distributedSessionId) {
243        this.restoreDistributedDataObject(want);
244      }
245    }
246  }
247
248  restoreDistributedDataObject(want: Want) {
249    if (!want.parameters || !want.parameters.distributedSessionId) {
250      console.error(TAG + 'missing sessionId');
251      return;
252    }
253
254    // 2.1 调用create接口创建并得到一个分布式数据对象实例
255    let attachment = this.createEmptyAttachment(); // 接收端需要将资产数据的各个属性设置为空字符串,才能恢复发起端保存的资产数据
256    let data = new Data(undefined, undefined, attachment);
257    dataObject = distributedDataObject.create(this.context, data);
258
259    // 2.2 注册恢复状态监听。收到状态为'restored'的回调通知时,表示接收端分布式数据对象已恢复发起端保存过来的数据(有资产数据时,对应的文件也迁移过来了)
260    dataObject.on('status', (sessionId: string, networkId: string, status: string) => {
261      if (status == 'restored') { // 收到'restored'的状态通知表示已恢复发起端保存的数据
262        console.log(TAG + `title: ${dataObject['title']}, text: ${dataObject['text']}`);
263      }
264    });
265
266    // 2.3 从want.parameters中获取发起端放入的sessionId,调用setSessionId接口设置同步的sessionId
267    let sessionId = want.parameters.distributedSessionId as string;
268    console.log(TAG + `get sessionId: ${sessionId}`);
269    dataObject.setSessionId(sessionId);
270  }
271
272  // 在分布式文件目录下创建一个文件并使用资产类型记录(也可以记录分布式文件目录下已有文件,非分布式文件目录下的文件可以复制或移动到分布式文件目录下再进行记录)
273  createAttachment() {
274    let attachment = this.createEmptyAttachment();
275    try {
276      let distributedDir: string = this.context.distributedFilesDir; // 分布式文件目录
277      let fileName: string = 'text_attachment.txt'; // 文件名
278      let filePath: string = distributedDir + '/' + fileName; // 文件路径
279      let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
280      fileIo.writeSync(file.fd, 'The text in attachment');
281      fileIo.closeSync(file.fd);
282      let uri: string = fileUri.getUriFromPath(filePath); // 获取文件URI
283      let stat = fileIo.statSync(filePath); // 获取文件详细属性信息
284
285      // 写入资产数据
286      attachment = {
287        name: fileName,
288        uri: uri,
289        path: filePath,
290        createTime: stat.ctime.toString(),
291        modifyTime: stat.mtime.toString(),
292        size: stat.size.toString()
293      }
294    } catch (e) {
295      let err = e as BusinessError;
296      console.error(TAG + `file error, error code: ${err.code}, error message: ${err.message}`);
297    }
298    return attachment;
299  }
300
301  createEmptyAttachment() {
302    let attachment: commonType.Asset = {
303      name: '',
304      uri: '',
305      path: '',
306      createTime: '',
307      modifyTime: '',
308      size: ''
309    }
310    return attachment;
311  }
312}
313```
314
315### 在多端协同中使用分布式数据对象
316
3171. 调用端调用startAbilityByCall接口拉起对端Ability:
318
319    1.1 调用genSessionId接口创建一个sessionId,通过分布式设备管理接口获取对端设备networkId。
320
321    1.2 组装want,并将sessionId放入want。
322
323    1.3 调用startAbilityByCall接口拉起对端Ability。
324
3252. 调用端拉起对端Ability后创建分布式数据对象并加入组网:
326
327   2.1 创建分布式数据对象实例。
328
329   2.2 注册数据变更监听。
330
331   2.3 设置同步sessionId加入组网。
332
3333. 被调用端被拉起后创建和恢复分布式数据对象:
334
335   3.1 创建分布式数据对象实例。
336
337   3.2 注册数据变更监听。
338
339   3.3 从want中获取源端放入的sessionId,使用这个sessionId加入组网。
340
341> **说明:**
342>
343> - 暂时只支持<!--RP3-->在[跨设备Call调用实现的多端协同](../application-models/hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)中使用分布式数据对象进行数据同步。<!--RP3End-->
344>
345> - 跨设备Call调用实现的多端协同开发<!--RP4-->需要申请`ohos.permission.DISTRIBUTED_DATASYNC`权限和配置单实例启动标签,详见跨设备Call调用实现的多端协同的[开发步骤](../application-models/hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)。<!--RP4End-->
346>
347> - wantParam中的"sessionId"字段可能被其他服务占用,建议自定义一个key存取sessionId。
348>
349> - 使用分布式设备管理获取对端设备networkId详见[设备信息查询开发指导](../distributedservice/devicemanager-guidelines.md#设备信息查询开发指导)。
350
351 示例代码如下:
352
353```ts
354import { AbilityConstant, Caller, common, UIAbility, Want } from '@kit.AbilityKit';
355import { hilog } from '@kit.PerformanceAnalysisKit';
356import { window } from '@kit.ArkUI';
357import { distributedDataObject } from '@kit.ArkData';
358import { distributedDeviceManager } from '@kit.DistributedServiceKit';
359import { BusinessError } from '@kit.BasicServicesKit';
360
361// 业务数据定义
362class Data {
363  title: string | undefined;
364  text: string | undefined;
365
366  constructor(title: string | undefined, text: string | undefined) {
367    this.title = title;
368    this.text = text;
369  }
370}
371
372const TAG = '[DistributedDataObject]';
373
374let sessionId: string;
375let caller: Caller;
376let dataObject: distributedDataObject.DataObject;
377
378export default class EntryAbility extends UIAbility {
379  // 1. 调用端调用startAbilityByCall接口拉起对端Ability
380  callRemote() {
381    if (caller) {
382      console.error(TAG + 'call remote already');
383      return;
384    }
385
386    // 1.1 调用genSessionId接口创建一个sessionId,通过分布式设备管理接口获取对端设备networkId
387    sessionId = distributedDataObject.genSessionId();
388    console.log(TAG + `gen sessionId: ${sessionId}`);
389    let deviceId = getRemoteDeviceId();
390    if (deviceId == "") {
391      console.warn(TAG + 'no remote device');
392      return;
393    }
394    console.log(TAG + `get remote deviceId: ${deviceId}`);
395
396    // 1.2 组装want,并将sessionId放入want
397    let want: Want = {
398      bundleName: 'com.example.collaboration',
399      abilityName: 'EntryAbility',
400      deviceId: deviceId,
401      parameters: {
402        'ohos.aafwk.param.callAbilityToForeground': true, // 前台启动,非必须
403        'distributedSessionId': sessionId
404      }
405    }
406    try {
407      // 1.3 调用startAbilityByCall接口拉起对端Ability
408      this.context.startAbilityByCall(want).then((res) => {
409        if (!res) {
410          console.error(TAG + 'startAbilityByCall failed');
411        }
412        caller = res;
413      })
414    } catch (e) {
415      let err = e as BusinessError;
416      console.error(TAG + `get remote deviceId error, error code: ${err.code}, error message: ${err.message}`);
417    }
418  }
419
420  // 2. 拉起对端Ability后创建分布式数据对象
421  createDataObject() {
422    if (!caller) {
423      console.error(TAG + 'call remote first');
424      return;
425    }
426    if (dataObject) {
427      console.error(TAG + 'create dataObject already');
428      return;
429    }
430
431    // 2.1 创建分布式数据对象实例
432    let data = new Data('The title', 'The text');
433    dataObject = distributedDataObject.create(this.context, data);
434
435    // 2.2 注册数据变更监听
436    dataObject.on('change', (sessionId: string, fields: Array<string>) => {
437      fields.forEach((field) => {
438        console.log(TAG + `${field}: ${dataObject[field]}`);
439      });
440    });
441    // 2.3 设置同步sessionId加入组网
442    dataObject.setSessionId(sessionId);
443  }
444
445  // 3. 被调用端被拉起后创建和恢复分布式数据对象
446  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
447    if (want.parameters && want.parameters.distributedSessionId) {
448      // 3.1 创建分布式数据对象实例
449      let data = new Data(undefined, undefined);
450      dataObject = distributedDataObject.create(this.context, data);
451
452      // 3.2 注册数据变更监听
453      dataObject.on('change', (sessionId: string, fields: Array<string>) => {
454        fields.forEach((field) => {
455          console.log(TAG + `${field}: ${dataObject[field]}`);
456        });
457      });
458      // 3.3 从want中获取源端放入的sessionId,使用这个sessionId加入组网
459      let sessionId = want.parameters.distributedSessionId as string;
460      console.log(TAG + `onCreate get sessionId: ${sessionId}`);
461      dataObject.setSessionId(sessionId);
462    }
463  }
464}
465
466// 获取可信组网中的设备
467function getRemoteDeviceId() {
468  let deviceId = "";
469  try {
470    let deviceManager = distributedDeviceManager.createDeviceManager('com.example.collaboration');
471    let devices = deviceManager.getAvailableDeviceListSync();
472    if (devices[0] && devices[0].networkId) {
473      deviceId = devices[0].networkId;
474    }
475  } catch (e) {
476    let err = e as BusinessError;
477    console.error(TAG + `get remote deviceId error, error code: ${err.code}, error message: ${err.message}`);
478  }
479  return deviceId;
480}
481```
482
483## 相关实例
484
485针对分布式数据对象开发,有以下相关实例可供参考:
486
487- [设备管理(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/DistributedAppDev/DistributedAuthentication)
488
489- [分布式备忘录(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedNote)