• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Cross-Device Sync of Distributed Data Objects (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## When to Use
11
12The traditional implementation of data sync between devices involves heavy workload. You need to design the message processing logic for setting up a communication link, sending, receiving, and processing messages, and resolving data conflicts, as well as retry mechanism upon errors. In addition, the debugging complexity increases with the number of devices.
13
14The device status, message sending progress, and data transmitted are variables. If these variables support global access, they can be accessed as local variables by difference devices. This simplifies data sync across devices.
15
16The distributed data object (**distributedDataObject**) module implements global access to variables. It provides basic data object management capabilities, including creating, querying, deleting, and modifying in-memory objects and subscribing to data or status changes. It also provides distributed capabilities. OpenHarmony provides easy-to-use JS APIs for distributed application scenarios. With these APIs, you can easily implement data collaboration for an application across devices and listening for status and data changes between devices. The **distributedDataObject** module implements data object collaboration for the same application across multiple devices that form a Super Device. It greatly reduces the development workloads compared with the traditional implementation.
17
18Currently, <!--RP2-->distributed data objects can be used only in [cross-device migration](../application-models/hop-cross-device-migration.md) and [multi-device collaboration using the cross-device call](../application-models/hop-multi-device-collaboration.md#using-cross-device-call).<!--RP2End-->
19
20## Basic Concepts
21
22- Distributed in-memory database: The distributed in-memory database caches data in the memory so that applications can quickly access the data. However, the data is not persisted. If the database is closed, the data is not retained.
23
24- Distributed data object: A distributed data object is encapsulation of a JS object type. Each distributed data object instance creates a data table in the in-memory database. The in-memory databases created for different applications are isolated from each other. Reading and writing a distributed data object are mapped to the **get** and **put** operations in the corresponding database, respectively.
25
26  The distributed data object has the following states in its lifecycle:
27
28  - **Uninitialized**: The distributed data object is not instantiated or is destroyed.
29  - **Local**: A data table is created, but the data cannot be synced.
30  - **Distributed**: A data table is created, and data can be synced (there are at least two online devices with the same session ID). If a device is offline or the session ID is empty, the distributed data object changes to the local state.
31
32
33## Working Principles
34
35**Figure 1** Working mechanism
36
37![distributedObject](figures/distributedObject.jpg)
38
39The distributed data objects are encapsulated JS objects in distributed in-memory databases, and can be operated in the same way as local variables. The system automatically implements data sync across devices.
40
41
42### Encapsulation and Storage of JS Objects
43
44- An in-memory database is created for each distributed data object instance and identified by a session ID (**SessionId**). The in-memory databases created for different applications are isolated from each other.
45
46- When a distributed data object is instantiated, all properties of the object are traversed recursively. **Object.defineProperty** is used to define the **set()** and **get()** methods for all properties. The **set()** and **get()** methods correspond to the **put** and **get** operations of a record in the database, respectively. **Key** specifies the property name, and **Value** specifies the property value.
47
48- When a distributed data object is read or written, the **get()** or **set()** method is automatically called to perform the related operation on data in the database.
49
50**Table 1** Correspondence between a distributed data object and a distributed database
51
52| Distributed Data Object Instance| Object Instance| Property Name| Property Value|
53| -------- | -------- | -------- | -------- |
54| Distributed in-memory database| Database identified by **sessionID**| Key of a record in the database| Value of a record in the database|
55
56
57### Cross-Device Sync and Data Change Notification
58
59One of the most important functions of distributed data objects is to implement data sync between objects. Distributed data objects are created locally for the devices on a trusted network. If the distributed data objects on different devices are set with the same **sessionID**, data can be synced between them.
60
61As shown in the following figure, distributed data object 1 of device A and distributed data object 1 of device B are set with the same session ID **session1**, and sync relationship of session 1 is established between the two objects.
62
63  **Figure 2** Object sync relationship
64
65![distributedObject_sync](figures/distributedObject_sync.jpg)
66
67For each device, only one distributed data object can be added to a sync relationship. As shown in the preceding figure, distributed data object 2 of device A cannot be added to session 1 because distributed data object 1 of device A has been added to session 1.
68
69After the sync relationship is established, each session has a copy of shared object data. The distributed data objects added to a session support the following operations:
70
71(1) Reading or modifying the data in the session.
72
73(2) Listening for data changes made by other devices.
74
75(3) Listening for status changes, such as the addition and removal of other devices.
76
77When a distributed data object is added to a session, if its data is different from that of the session, the distributed data object updates data of the session. If you do not want to update the data of the session when adding a distributed data object to a session and obtain the data of the session, set the attribute value of the object to **undefined** (for an asset, set each attribute of the asset to an empty string).
78
79### Minimum Sync Unit
80
81Property is the minimum unit to synchronize in distributed data objects. For example, object 1 in the following figure has three properties: name, age, and parents. If one of the properties is changed, only the changed property needs to be synced.
82
83The object properties support basic types (number, Boolean, and string) and complex types (array and nested basic types). For the distributed data object of the complex type, only the root property can be modified. The subordinate properties cannot be modified.
84
85```ts
86dataObject['parents'] = {mom: "amy"}; // Supported modification
87dataObject['parents']['mom'] = "amy"; // Unsupported modification
88```
89
90**Figure 3** Sync of distributed data objects
91
92
93![distributedObject_syncView](figures/distributedObject_syncView.jpg)
94
95
96### Persistence of Distributed Data Objects
97
98Distributed data objects run in the process space of applications. After the data of a distributed data object is persisted in the distributed database, the data will not be lost after the application exits.
99
100You need to persist distributed data objects in the following scenarios:
101
102- Enable an application to retrieve the exact same data after it starts again. In this case, you need to persist the distributed data object (for example, object 1 with session ID 1). After the application starts again, create a distributed data object (for example, object 2) and set the session ID to 1. Then, the application can retrieve the data of object 1.
103
104- Enable an application started on another device to retrieve the exact same data. In this case, you need to persist the distributed data object (for example, object 1 with session ID 1) on device A and synchronize the data to device B. Then, create a distributed data object (for example, object 2) and set the session ID to 1. When the application is started on device B, it can retrieve the same application data used on device A before the application is closed.
105
106### Asset Sync Mechanism
107
108In a distributed data object, [asset](../reference/apis-arkdata/js-apis-data-commonType.md#asset) is used to describe a local entity asset file. When the distributed data object is synced across devices, the file is also synced to other devices with it.
109
110In versions earlier than API version 20, only asset is supported, and [assets](../reference/apis-arkdata/js-apis-data-commonType.md#assets) are not supported. To synchronize multiple assets, use each asset as a root property of the distributed data object.
111
112Since API version 20, synchronization of [assets](../reference/apis-arkdata/js-apis-data-commonType.md#assets) is supported.
113
114## Constraints
115<!--RP5-->
116- Currently, distributed data objects can be used only in [cross-device migration](../application-models/hop-cross-device-migration.md) and [multi-device collaboration using the cross-device call](../application-models/hop-multi-device-collaboration.md#using-cross-device-call).
117
118- Currently, the cross-device continuation capability supports the following scenarios:
119  - [Migrating between abilities in the same application across devices](../application-models/hop-cross-device-migration.md#migrating-between-abilities-in-the-same-application-across-devices)
120  - [Migrating abilities with different bundle names in the same application across devices](../application-models/hop-cross-device-migration.md#migrating-abilities-with-different-bundle-names-in-the-same-application-across-devices)
121<!--RP5End-->
122- Data can be synced for the distributed data objects with the same session ID.
123
124- Each distributed data object occupies 100 KB to 150 KB of memory. Therefore, you are advised not to create too many distributed data objects.
125
126- The maximum size of a distributed data object is 500 KB.
127
128- If data of 1 KB data is modified on device A, device B can complete data update within 50 ms after receiving a data change notification.
129
130- A maximum of 16 distributed data object instances can be created for an application.
131
132- For the sake of performance and user experience, the maximum number of devices for data collaboration is 3.
133
134- For the distributed data object of the complex type, only the root property can be modified. The subordinate properties cannot be modified. In [asset sync mechanism](#asset-sync-mechanism), the data of the asset type must support modification of its lower-level properties.
135
136- Currently, only JS APIs are supported.
137
138## Available APIs
139
140Most of the APIs for cross-device sync of distributed data objects are executed asynchronously in callback or promise mode. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed Data Object](../reference/apis-arkdata/js-apis-data-distributedobject.md).
141
142| API| Description|
143| -------- | -------- |
144| create(context: Context, source: object): DataObject | Creates a distributed data object instance.|
145| genSessionId(): string | Generates a session ID for distributed data objects.|
146| setSessionId(sessionId: string, callback: AsyncCallback&lt;void&gt;): void | Sets a session ID for data sync. Automatic sync is performed for devices with the same session ID on a trusted network.|
147| setSessionId(callback: AsyncCallback&lt;void&gt;): void | Exits all sessions.|
148| on(type: 'change', callback: (sessionId: string, fields: Array&lt;string&gt;) => void): void | Subscribes to data changes of the distributed data object.|
149| off(type: 'change', callback?: (sessionId: string, fields: Array&lt;string&gt;) => void): void | Unsubscribes from data changes of the distributed data object.|
150| on(type: 'status', callback: (sessionId: string, networkId: string, status: 'online' \| 'offline' ) => void): void | Subscribes to status changes of the distributed data object.|
151| off(type: 'status', callback?: (sessionId: string, networkId: string, status: 'online' \|'offline' ) => void): void | Unsubscribes from status changes of the distributed data object.|
152| save(deviceId: string, callback: AsyncCallback&lt;SaveSuccessResponse&gt;): void | Saves a distributed data object.|
153| revokeSave(callback: AsyncCallback&lt;RevokeSaveSuccessResponse&gt;): void | Revokes the saving of the distributed data object.|
154| bindAssetStore(assetKey: string, bindInfo: BindInfo, callback: AsyncCallback&lt;void&gt;): void | Binds an asset and its RDB store.|
155| setAsset(assetKey: string, uri: string): void | Sets an asset.|
156| setAssets(assetKey: string, uris: Array&lt;string&gt;): void | Sets assets.|
157| on(type: 'change', callback: DataObserver&lt;void&gt;): void | Subscribes to data changes of this distributed data object.|
158| off(type: 'change', callback?: DataObserver&lt;void&gt;): void |  Unsubscribes from data changes of this distributed data object.|
159| on(type: 'status', callback: StatusObserver&lt;void&gt;): void | Subscribes to the status changes of this distributed data object.|
160| off(type: 'status', callback?: StatusObserver&lt;void&gt;): void | Unsubscribes from status changes of this distributed data object.|
161
162
163## How to Develop
164
165### Using Distributed Data Objects in Cross-Device Migration
166
1671. Create a distributed data object in **onContinue()** for the application on the source device, and save data.
168
169    1.1 Call **create()** to create a distributed data object instance.
170
171    1.2 Call **genSessionId()** to generate a **sessionId**, call **setSessionId()** to set a **sessionId**, and add the **sessionId** to **wantParam**. The distributed data objects with the same **sessionId** can connect to the same network.
172
173    1.3 Obtain the **networkId** from **wantParam** for the application on the target device and call **save()** with this **networkId** to save data to the target device.
174
1752. Create a distributed data object in **onCreate()** and **onNewWant()** for the application on the target device, and register a listener for the "restored" state.
176
177    2.1 Call **create()** to create a distributed data object instance for the application on the target device.
178
179    2.2 Register a listener callback for the data recovery state. If "restored" is returned by the listener callback registered, the distributed data object of the target device has obtained the data transferred from the source device.
180
181    2.3 Obtain the **sessionId** of the source device from **want.parameters** and call **setSessionId** to set the same **sessionId** for the target device.
182
183> **NOTE**
184>
185> - In cross-device migration, after **setSessionId()** is called on the source device to set **sessionId**, you should call **save()** to save data to the target device.
186> - When an application is launched as a result of a migration, the [onWindowStageRestore()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onwindowstagerestore) lifecycle callback function, rather than [onWindowStageCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onwindowstagecreate), is triggered following **onCreate()** or **onNewWant()**. This sequence occurs for both cold and hot starts. If you have performed some necessary initialization operations during application launch in **onWindowStageCreate()**, you must perform the same initialization operations in **onWindowStageRestore()** after the migration to avoid application exceptions.
187>
188<!--RP1-->
189> - The **continuable** tag must be set for cross-device migration. For details, see [How to Develop](../application-models/hop-cross-device-migration.md#how-to-develop).<!--RP1End-->
190>
191> - The **sessionId** field in **wantParam** is used by other services. You are advised to customize a key for accessing the **sessionId** field.
192>
193> - Use data of the Asset type to record information about assets (such as documents, images, and videos). When asset data is migrated, the corresponding asset is also migrated to the target device.
194>
195> - The initial value of the service data must be set to **undefined** on the target device so that the data saved on the source device can be restored on the target device. Otherwise, the data on the source device will be overwritten by the data set on the target device. For asset data, you need to set each attribute of the asset data to an empty string instead of setting the entire asset data to **undefined**.
196>
197> - Currently, the asset array is not supported. If multiple files need to be migrated, define an asset data record for each file to migrate.
198>
199> - Currently, only files in distributed file directory can be migrated. Files in other directories can be copied or moved to distributed file directory before migration. For details about how to move or copy files and obtain URIs, see [File Management](../reference/apis-core-file-kit/js-apis-file-fs.md) and [File URI](../reference/apis-core-file-kit/js-apis-file-fileuri.md).
200
201```ts
202import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
203import { commonType, distributedDataObject } from '@kit.ArkData';
204import { BusinessError } from '@kit.BasicServicesKit';
205
206// Define service data.
207export class ContentInfo {
208  mainTitle: string | undefined;
209  textContent: string | undefined;
210  imageUriArray: Array<ImageInfo> | undefined;
211  isShowLocalInfo: boolean | undefined;
212  isAddLocalInfo: boolean | undefined;
213  selectLocalInfo: string | undefined;
214  attachments?: commonType.Assets | undefined;
215
216  constructor(
217    mainTitle: string | undefined,
218    textContent: string | undefined,
219    imageUriArray: Array<ImageInfo>| undefined,
220    isShowLocalInfo: boolean | undefined,
221    isAddLocalInfo: boolean | undefined,
222    selectLocalInfo: string | undefined,
223    attachments?: commonType.Assets | undefined
224  ) {
225    this.mainTitle = mainTitle;
226    this.textContent = textContent;
227    this.imageUriArray = imageUriArray;
228    this.isShowLocalInfo = isShowLocalInfo;
229    this.isAddLocalInfo = isAddLocalInfo;
230    this.selectLocalInfo = selectLocalInfo;
231    this.attachments = attachments;
232  }
233
234  flatAssets(): object {
235    let obj: object = this;
236    if (!this.attachments) {
237      return obj;
238    }
239    for (let i = 0; i < this.attachments.length; i++) {
240      obj[`attachments${i}`] = this.attachments[i];
241    }
242    return obj;
243  }
244}
245
246export interface ImageInfo {
247  /**
248   * image PixelMap.
249   */
250  imagePixelMap: PixelMap;
251
252  /**
253   * Image name.
254   */
255  imageName: string;
256}
257
258const TAG = '[DistributedDataObject]';
259let dataObject: distributedDataObject.DataObject;
260
261export default class EntryAbility extends UIAbility {
262  private imageUriArray: Array<ImageInfo> = [];
263  private distributedObject: distributedDataObject.DataObject | undefined = undefined;
264  // 1. Create a distributed data object in **onContinue()** for the application on the source device, and save data to the target device.
265  async onContinue(wantParam: Record<string, Object | undefined>): Promise<AbilityConstant.OnContinueResult> {
266    // 1.1 Obtain the key URI of the distributed data object to be set.
267    try {
268      let sessionId: string = distributedDataObject.genSessionId();
269      wantParam.distributedSessionId = sessionId;
270
271      let distrUriArray: Array<string> = [];
272      let assetUriArray = AppStorage.get<Array<string>>('assetUriArray');
273        if (assetUriArray) {
274          distrUriArray = assetUriArray;
275        }
276      // 1.2 Create a distributed data object.
277      let contentInfo: ContentInfo = new ContentInfo(
278        AppStorage.get('mainTitle'),
279        AppStorage.get('textContent'),
280        AppStorage.get('imageUriArray'),
281        AppStorage.get('isShowLocalInfo'),
282        AppStorage.get('isAddLocalInfo'),
283        AppStorage.get('selectLocalInfo'),
284      );
285      let source = contentInfo.flatAssets();
286      this.distributedObject = distributedDataObject.create(this.context, source);
287
288      // 1.3 Set the asset or assets of the distributed data object.
289      if (assetUriArray?.length == 1) {
290        this.distributedObject?.setAsset('attachments', distrUriArray[0]). then(() => {
291          console.info('OnContinue setAsset');
292        })
293      } else {
294        this.distributedObject?.setAssets('attachments', distrUriArray). then(() => {
295          console.info('OnContinue setAssets');
296        })
297      }
298      // 1.4 Save the asset or assets to the source device.
299      this.distributedObject?.setSessionId(sessionId);
300      this.distributedObject?.save(wantParam.targetDevice as string).catch((err: BusinessError) => {
301        console.error('OnContinue failed to save. code: ', err.code);
302        console.error('OnContinue failed to save. message: ', err.message);
303      });
304    } catch (error) {
305      console.error('OnContinue faild code: ', error.code);
306      console.error('OnContinue faild message: ', error.message);
307    }
308    console.info("OnContinue success!");
309    return AbilityConstant.OnContinueResult.AGREE;
310  }
311
312  // 2. Create a distributed data object in onCreate() for the application on the target device (for cold start), and add it to the network for data migration.
313  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
314    if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
315      if (want.parameters && want.parameters.distributedSessionId) {
316        this.restoreDistributedObject(want);
317      }
318    }
319  }
320
321  // 2. Create a distributed data object in onNewWant() for the application on the target device (for hot start), and add it to the network for data migration.
322  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
323    if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
324      if (want.parameters && want.parameters.distributedSessionId) {
325        this.restoreDistributedObject(want);
326      }
327    }
328  }
329
330  async restoreDistributedObject(want: Want): Promise<void> {
331    if (!want.parameters || !want.parameters.distributedSessionId) {
332      console.error(TAG + 'missing sessionId');
333      return;
334    }
335
336    // 2.1 Call create() to create a distributed data object instance for the application on the target device.
337    let mailInfo: ContentInfo = new ContentInfo(undefined, undefined, [], undefined, undefined, undefined, undefined);
338    dataObject = distributedDataObject.create(this.context, mailInfo);
339
340    // 2.2 Register a listener callback for the data recovery state. If "restored" is returned by the listener callback registered, the distributed data object of the target device has obtained the data transferred from the source device. If asset data is migrated, the file is also transferred to the target device.
341    dataObject.on('status', (sessionId: string, networkId: string, status: string) => {
342      console.log(TAG + `status change, sessionId:  ${sessionId}`);
343      console.log(TAG + `status change, networkId:  ${networkId}`);
344      if (status == 'restored') { // "restored" indicates that the data saved on the source device is restored on the target device.
345        console.log(TAG + `title: ${dataObject['title']}, text: ${dataObject['text']}`);
346        AppStorage.setOrCreate('mainTitle', dataObject['mainTitle']);
347        AppStorage.setOrCreate('textContent', dataObject['textContent']);
348        AppStorage.setOrCreate('imageUriArray', dataObject['imageUriArray']);
349        AppStorage.setOrCreate('isShowLocalInfo', dataObject['isShowLocalInfo']);
350        AppStorage.setOrCreate('isAddLocalInfo', dataObject['isAddLocalInfo']);
351        AppStorage.setOrCreate('selectLocalInfo', dataObject['selectLocalInfo']);
352        AppStorage.setOrCreate<Array<ImageInfo>>('imageUriArray', this.imageUriArray);
353      }
354    });
355
356    // 2.3 Obtain the sessionId of the source device from want.parameters and call setSessionId to set the same sessionId for the target device.
357    let sessionId = want.parameters.distributedSessionId as string;
358    console.log(TAG + `get sessionId: ${sessionId}`);
359    dataObject.setSessionId(sessionId);
360  }
361}
362```
363
364### Using Distributed Data Objects in Multi-Device Collaboration
365
3661. Call **startAbilityByCall()** to start an ability on another device.
367
368    1.1 Call **genSessionId()** to create a **sessionId** and obtain a **networkId** of the peer device through the distributed device management API.
369
370    1.2 Assemble **want** and put **sessionId** into **want**.
371
372    1.3 Call **startAbilityByCall()** to start the peer ability.
373
3742. Create a distributed data object on the caller device and adds it to the network.
375
376   2.1 Create a distributed data object instance.
377
378   2.2 Register a listener callback for data changes.
379
380   2.3 Set a **sessionId** for the distributed data object and add it to the network.
381
3823. Create a distributed data object on the peer device and restore the data saved on the caller device.
383
384   3.1 Create a distributed data object instance on the peer device.
385
386   3.2 Register a listener callback for data changes.
387
388   3.3 Obtain **sessionId** of the caller device from **want** and add the distributed data object instance to the network with the **sessionId**.
389
390> **NOTE**
391>
392> - Currently, <!--RP3-->distributed data objects can be used only in [multi-device collaboration using the cross-device call](../application-models/hop-multi-device-collaboration.md#using-cross-device-call) to sync data.<!--RP3End-->
393>
394> - To implement multi-device collaboration using the cross-device call, <!--RP4-->you need to apply for the ohos.permission.DISTRIBUTED_DATASYNC permission and set **launchType** to **singleton**. For details, see [How to Develop](../application-models/hop-multi-device-collaboration.md#using-cross-device-call).<!--RP4End-->
395>
396> - The **sessionId** field in **wantParam** is used by other services. You are advised to customize a key for accessing the **sessionId** field.
397>
398> - For details about how to obtain the **networkId** of the peer device, see [Querying Device Information](../distributedservice/devicemanager-guidelines.md#querying-device-information).
399
400 The sample code is as follows:
401
402```ts
403import { AbilityConstant, Caller, UIAbility, Want } from '@kit.AbilityKit';
404import { distributedDataObject } from '@kit.ArkData';
405import { distributedDeviceManager } from '@kit.DistributedServiceKit';
406import { BusinessError } from '@kit.BasicServicesKit';
407import { JSON } from '@kit.ArkTS';
408
409// Define service data.
410class Data {
411  title: string | undefined;
412  text: string | undefined;
413
414  constructor(title: string | undefined, text: string | undefined) {
415    this.title = title;
416    this.text = text;
417  }
418}
419
420const TAG = '[DistributedDataObject]';
421
422let sessionId: string;
423let caller: Caller;
424let dataObject: distributedDataObject.DataObject;
425const changeCallBack: distributedDataObject.DataObserver = (sessionId: string, fields: Array<string>) => {
426  console.info(`change, sessionId: ${sessionId}, fields: ${JSON.stringify(fields)}`);
427}
428
429export default class EntryAbility extends UIAbility {
430  // 1. Call startAbilityByCall() to start an ability on another device.
431  callRemote() {
432    if (caller) {
433      console.error(TAG + 'call remote already');
434      return;
435    }
436
437    // 1.1 Call genSessionId() to create a sessionId and call getRemoteDeviceId() to obtain the networkId of the peer device.
438    sessionId = distributedDataObject.genSessionId();
439    console.log(TAG + `gen sessionId: ${sessionId}`);
440    let deviceId = getRemoteDeviceId();
441    if (deviceId == "") {
442      console.warn(TAG + 'no remote device');
443      return;
444    }
445    console.log(TAG + `get remote deviceId: ${deviceId}`);
446
447    // 1.2 Assemble want and put sessionId into want.
448    let want: Want = {
449      bundleName: 'com.example.collaboration',
450      abilityName: 'EntryAbility',
451      deviceId: deviceId,
452      parameters: {
453        'ohos.aafwk.param.callAbilityToForeground': true, // Start the ability in the foreground. This parameter is optional.
454        'distributedSessionId': sessionId
455      }
456    }
457    try {
458      // 1.3 Call startAbilityByCall() to start the peer ability.
459      this.context.startAbilityByCall(want).then((res) => {
460        if (!res) {
461          console.error(TAG + 'startAbilityByCall failed');
462        }
463        caller = res;
464      })
465    } catch (e) {
466      let err = e as BusinessError;
467      console.error(TAG + `get remote deviceId error, error code: ${err.code}, error message: ${err.message}`);
468    }
469  }
470
471  // 2. Create a distributed data object after starting the peer ability.
472  createDataObject() {
473    if (!caller) {
474      console.error(TAG + 'call remote first');
475      return;
476    }
477    if (dataObject) {
478      console.error(TAG + 'create dataObject already');
479      return;
480    }
481
482    // 2.1 Create a distributed data object instance.
483    let data = new Data('The title', 'The text');
484    dataObject = distributedDataObject.create(this.context, data);
485
486    // 2.2 Register a listener callback for data changes.
487    dataObject.on('change', changeCallBack);
488    // 2.3 Set a sessionId for the distributed data object and add it to the network.
489    dataObject.setSessionId(sessionId);
490  }
491
492  // 3. Create a distributed data object on the peer device and restore the data saved on the caller device.
493  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
494    if (want.parameters && want.parameters.distributedSessionId) {
495      // 3.1 Create a distributed data object instance on the peer device.
496      let data = new Data(undefined, undefined);
497      dataObject = distributedDataObject.create(this.context, data);
498
499      // 3.2 Register a listener callback for data changes.
500      dataObject.on('change', changeCallBack);
501      // 3.3 Obtain sessionId of the caller device from **want** and add the distributed data object instance to the network with the sessionId.
502      let sessionId = want.parameters.distributedSessionId as string;
503      console.log(TAG + `onCreate get sessionId: ${sessionId}`);
504      dataObject.setSessionId(sessionId);
505    }
506  }
507}
508
509// Obtain devices on the trusted network.
510function getRemoteDeviceId() {
511  let deviceId = "";
512  try {
513    let deviceManager = distributedDeviceManager.createDeviceManager('com.example.collaboration');
514    let devices = deviceManager.getAvailableDeviceListSync();
515    if (devices[0] && devices[0].networkId) {
516      deviceId = devices[0].networkId;
517    }
518  } catch (e) {
519    let err = e as BusinessError;
520    console.error(TAG + `get remote deviceId error, error code: ${err.code}, error message: ${err.message}`);
521  }
522  return deviceId;
523}
524```