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