1# Cross-Device Migration
2
3## Overview
4
5When the environment changes, for example, when a user goes outdoors or when a more appropriate device is detected, the user can migrate an ongoing task to another device for better experience. The application on the source device can automatically exit, depending on the setting. A typical cross-device migration scenario is as follows: The user migrates a video playback task from a tablet to a smart TV. The video application on the tablet exits. From the perspective of application development, cross-device migration enables the [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) component running on device A to migrate to and keep running on device B. After the migration is complete, the UIAbility component on device A automatically exits (depending on the setting).
6
7Cross-device migration supports the following features:
8
9- Saving and restoring custom data
10
11- Saving and restoring page routing information and page component status data
12
13- Checking application compatibility
14
15- Dynamically setting the migration state (**ACTIVE** by default)
16
17  For example, for an editing application, only the text editing page needs to be migrated to the target device. In this case, you can call [setMissionContinueState](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissioncontinuestate10) for precise control.
18
19- Determining whether to restore the page stack (restored by default)
20
21 If an application wants to customize the page to be displayed after being migrated to the target device, you can use [SUPPORT_CONTINUE_PAGE_STACK_KEY](../reference/apis-ability-kit/js-apis-app-ability-wantConstant.md#wantconstantparams) for precise control.
22
23- Determining whether to exit the application on the source device after a successful migration (application exit by default)
24
25 You can use [SUPPORT_CONTINUE_SOURCE_EXIT_KEY](../reference/apis-ability-kit/js-apis-app-ability-wantConstant.md#wantconstantparams) for precise control.
26
27  > **NOTE**
28  >
29  > You only need to develop an application with the migration capabilities. System applications will trigger cross-device migration.
30
31## Working Principles
32
33![hop-cross-device-migration](figures/hop-cross-device-migration.png)
34
351. On the source device, the [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) uses the [onContinue()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncontinue) callback to save service data to be migrated. For example, to complete cross-device migration, a browser application uses the **onContinue()** callback to save service data such as the page URL.
362. The distributed framework provides a mechanism for saving and restoring application page stacks and service data across devices. It sends the data from the source device to the target device.
373. On the target device, the same UIAbility uses the [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate) callback (in the case of cold start) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant) callback (in the case of hot start) to restore the service data.
38
39## Cross-Device Migration Process
40
41The following figure shows the cross-device migration process when a migration request is initiated from the migration entry of the peer device.
42
43![hop-cross-device-migration](figures/hop-cross-device-migration4.png)
44
45## Constraints
46
47- Cross-device migration must be performed between the same [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md) component. In other words, **bundleName**, **abilityName**, and **signature** of the component on the two devices must be the same.
48- For better user experience, the data to be transmitted via the **wantParam** parameter must be less than 100 KB.
49
50## How to Develop
51
521. Configure the **continuable** tag under **abilities** in the [module.json5 file](../quick-start/module-configuration-file.md).
53
54    ```json
55    {
56      "module": {
57        // ...
58        "abilities": [
59          {
60            // ...
61            "continuable": true, // Configure the UIAbility to support migration.
62          }
63        ]
64      }
65    }
66    ```
67
68   > **NOTE**
69   >
70   > Configure the application launch type. For details, see [UIAbility Launch Type](uiability-launch-type.md).
71
722. Implement **onContinue()** in the UIAbility on the source device.
73
74    When a migration is triggered for the [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md), [onContinue()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncontinue) is called on the source device. You can use either synchronous or asynchronous mode to save the data in this callback to implement application compatibility check and migration decision.
75
76    - Saving data to migrate: You can save the data to migrate in key-value pairs in **wantParam**.
77    - Checking application compatibility: You can obtain the application version on the target device from **wantParam.version** in the **onContinue()** callback and compare it with the application version on the source device.
78
79    - Making a migration decision: You can determine whether migration is supported based on the return value of **onContinue()**. For details about the return values, see [AbilityConstant.OnContinueResult](../reference/apis-ability-kit/js-apis-app-ability-abilityConstant.md#abilityconstantoncontinueresult).
80
81    Certain fields (listed in the table below) in **wantParam** passed in to **onContinue()** are preconfigured in the system. You can use these fields for service processing. When defining custom `wantParam` data, do not use the same keys as the preconfigured ones to prevent data exceptions due to system overwrites.  
82    | Field|Description|
83    | ---- | ---- |
84    | version | Version the application on the target device.|
85    | targetDevice | Network ID of the target device.|
86
87    ```ts
88    import { AbilityConstant, UIAbility } from '@kit.AbilityKit';
89    import { hilog } from '@kit.PerformanceAnalysisKit';
90
91    const TAG: string = '[MigrationAbility]';
92    const DOMAIN_NUMBER: number = 0xFF00;
93
94    export default class MigrationAbility extends UIAbility {
95      // Prepare data to migrate in onContinue.
96      onContinue(wantParam: Record<string, Object>):AbilityConstant.OnContinueResult {
97        let targetVersion = wantParam.version;
98        let targetDevice = wantParam.targetDevice;
99        hilog.info(DOMAIN_NUMBER, TAG, `onContinue version = ${targetVersion}, targetDevice: ${targetDevice}`);
100
101        // Obtain the application version on the source device.
102        let versionSrc: number = -1; // Enter the version number obtained.
103
104        // Compatibility verification
105        if (targetVersion !== versionSrc) {
106          // Return MISMATCH when the compatibility check fails.
107          return AbilityConstant.OnContinueResult.MISMATCH;
108        }
109
110        // Save the data to migrate in the custom field (for example, data) of wantParam.
111        const continueInput = 'Data to migrate';
112        wantParam['data'] = continueInput;
113
114        return AbilityConstant.OnContinueResult.AGREE;
115      }
116    }
117    ```
118
1193. For the UIAbility on the target device, implement [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant) to restore the data and load the UI.
120
121    The APIs to call vary according to the launch types, as shown below.
122
123    ![hop-cross-device-migration](figures/hop-cross-device-migration5.png)
124
125    > **NOTE**
126    > 
127    > When an application is launched as a result of a migration, the [onWindowStageRestore()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagerestore) lifecycle callback function, rather than [onWindowStageCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagecreate), is triggered following **onCreate()** or **onNewWant()**. This sequence occurs for both cold and hot starts.
128    > 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.
129
130    - The **launchReason** parameter in the **onCreate()** or **onNewWant()** callback specifies whether the launch is triggered as a result of a migration.
131    - You can obtain the saved data from the [want](../reference/apis-ability-kit/js-apis-app-ability-want.md) parameter.
132    - To use system-level page stack restoration, you must call [restoreWindowStage()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextrestorewindowstage) to trigger page restoration with page stacks before **onCreate()** or **onNewWant()** is complete. For details, see [On-Demand Page Stack Migration](./hop-cross-device-migration.md#on-demand-page-stack-migration).
133
134    ```ts
135    import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
136    import { hilog } from '@kit.PerformanceAnalysisKit';
137
138    const TAG: string = '[MigrationAbility]';
139    const DOMAIN_NUMBER: number = 0xFF00;
140
141    export default class MigrationAbility extends UIAbility {
142      storage : LocalStorage = new LocalStorage();
143
144      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
145        hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onCreate');
146        if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
147          // Restore the saved data from want.parameters.
148          let continueInput = '';
149          if (want.parameters !== undefined) {
150            continueInput = JSON.stringify(want.parameters.data);
151            hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);
152          }
153          // Trigger page restoration.
154          this.context.restoreWindowStage(this.storage);
155        }
156      }
157
158      onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
159          hilog.info(DOMAIN_NUMBER, TAG, 'onNewWant');
160          if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
161            // Restore the saved data from want.parameters.
162            let continueInput = '';
163            if (want.parameters !== undefined) {
164              continueInput = JSON.stringify(want.parameters.data);
165              hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);
166            }
167            // Trigger page restoration.
168            this.context.restoreWindowStage(this.storage);
169          }
170        }
171    }
172    ```
173
174## Configuring Optional Migration Features
175
176### Dynamically Setting the Migration State
177
178Since API version 10, you can dynamically set the migration state. Specifically, you can enable or disable migration as required by calling [setMissionContinueState()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissioncontinuestate10). By default, **ACTIVE** is set for an application, indicating that migration is enabled.
179
180**Time for Setting the Migration State**
181
182To implement special scenarios, for example, where migration is required only for a specific page or upon a specific event, perform the following steps:
183
1841. In the [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate) lifecycle callback of the [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md), disable cross-device migration.
185
186    ```ts
187    // MigrationAbility.ets
188    import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
189    import { hilog } from '@kit.PerformanceAnalysisKit';
190
191    const TAG: string = '[MigrationAbility]';
192    const DOMAIN_NUMBER: number = 0xFF00;
193
194    export default class MigrationAbility extends UIAbility {
195      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
196        // ...
197        this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
198          hilog.info(DOMAIN_NUMBER, TAG, `setMissionContinueState INACTIVE result: ${JSON.stringify(result)}`);
199        });
200        // ...
201      }
202    }
203    ```
204
2052. To enable cross-device migration for a specific page, call the migration API in [onPageShow()](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onpageshow) of the page.
206
207    ```ts
208    // Page_MigrationAbilityFirst.ets
209    import { AbilityConstant, common } from '@kit.AbilityKit';
210    import { hilog } from '@kit.PerformanceAnalysisKit';
211
212    const TAG: string = '[MigrationAbility]';
213    const DOMAIN_NUMBER: number = 0xFF00;
214
215    @Entry
216    @Component
217    struct Page_MigrationAbilityFirst {
218      private context = getContext(this) as common.UIAbilityContext;
219      build() {
220        // ...
221      }
222      // ...
223      onPageShow(){
224        // When the page is displayed, set the migration state to ACTIVE.
225        this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
226          hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
227        });
228      }
229    }
230    ```
231
2323. To enable cross-device migration for a specific event, call the migration API in the event. The following uses the [onClick()](../reference/apis-arkui/arkui-ts/ts-universal-events-click.md#onclick) event of the **Button** component as an example:
233
234    ```ts
235    // Page_MigrationAbilityFirst.ets
236    import { AbilityConstant, common } from '@kit.AbilityKit';
237    import { hilog } from '@kit.PerformanceAnalysisKit';
238    import { promptAction } from '@kit.ArkUI';
239
240    const TAG: string = '[MigrationAbility]';
241    const DOMAIN_NUMBER: number = 0xFF00;
242
243    @Entry
244    @Component
245    struct Page_MigrationAbilityFirst {
246      private context = getContext(this) as common.UIAbilityContext;
247      build() {
248        Column() {
249          //...
250          List({ initialIndex: 0 }) {
251            ListItem() {
252              Row() {
253                //...
254              }
255              .onClick(() => {
256                // When the button is clicked, set the migration state to ACTIVE.
257                this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
258                  hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
259                  promptAction.showToast({
260                    message: 'Success'
261                  });
262                });
263              })
264            }
265            //...
266          }
267          //...
268        }
269        //...
270      }
271    }
272    ```
273
274### Migrating the Page Stack on Demand
275
276
277> **NOTE**
278>
279> Currently, only the page stack implemented based on the router module can be automatically restored. The page stack implemented using the **Navigation** component cannot be automatically restored.
280> If an application uses the **Navigation** component for routing, you can disable default page stack migration by setting [SUPPORT_CONTINUE_PAGE_STACK_KEY](../reference/apis-ability-kit/js-apis-app-ability-wantConstant.md#wantconstantparams) to **false**. In addition, save the page (or page stack) to be migrated in **want**, and manually load the specified page on the target device.
281
282By default, the page stack is restored during the migration of a [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md). Before [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant) finishes the execution, call [restoreWindowStage()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#restore) to pass in the current window context, which will be used for page stack loading and restoration. The **restoreWindowStage()** API must be executed in a synchronous API. If it is executed during an asynchronous callback, there is a possibility that the page fails to be loaded during application startup.
283
284The following uses **onCreate()** as an example:
285
286```ts
287import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
288
289export default class MigrationAbility extends UIAbility {
290  storage : LocalStorage = new LocalStorage();
291
292  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
293      // ...
294      // Trigger page restoration before the synchronous API finishes the execution.
295      this.context.restoreWindowStage(this.storage);
296  }
297}
298```
299
300If the application does not want to use the page stack restored by the system, set [SUPPORT_CONTINUE_PAGE_STACK_KEY](../reference/apis-ability-kit/js-apis-app-ability-wantConstant.md#wantconstantparams) to **false**, and specify the page to be displayed after the migration in [onWindowStageRestore()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagerestore). If the page to be displayed after the migration is not specified, a blank page will be displayed.
301
302For example, a UIAbility does not want to restore the page stack displayed on the source device. Instead, it wants **Page_MigrationAbilityThird** to be restored after the migration.
303
304```ts
305// MigrationAbility.ets
306import { AbilityConstant, UIAbility, wantConstant } from '@kit.AbilityKit';
307import { hilog } from '@kit.PerformanceAnalysisKit';
308import { window } from '@kit.ArkUI';
309
310const TAG: string = '[MigrationAbility]';
311const DOMAIN_NUMBER: number = 0xFF00;
312
313export default class MigrationAbility extends UIAbility {
314  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
315    // ...
316    // Configure not to use the system page stack during restoration.
317    wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;
318    return AbilityConstant.OnContinueResult.AGREE;
319  }
320
321  onWindowStageRestore(windowStage: window.WindowStage): void {
322    // If the system page stack is not used for restoration, specify the page to be displayed after the migration.
323    windowStage.loadContent('pages/page_migrationability/Page_MigrationAbilityThird', (err, data) => {
324      if (err.code) {
325        hilog.error(DOMAIN_NUMBER, TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
326        return;
327      }
328    });
329  }
330}
331```
332
333### Exiting the Application on Demand
334
335By default, the application on the source device exits after the migration is complete. If you want the application to continue running on the source device after the migration or perform other operations (such as saving drafts and clearing resources) before exiting on the source device, you can set [SUPPORT_CONTINUE_SOURCE_EXIT_KEY](../reference/apis-ability-kit/js-apis-app-ability-wantConstant.md#wantconstantparams) to **false**.
336
337Example: A UIAbility on the source device does not exit after a successful migration.
338
339```ts
340import { AbilityConstant, UIAbility, wantConstant } from '@kit.AbilityKit';
341import { hilog } from '@kit.PerformanceAnalysisKit';
342
343const TAG: string = '[MigrationAbility]';
344const DOMAIN_NUMBER: number = 0xFF00;
345
346export default class MigrationAbility extends UIAbility {
347  // ...
348  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
349    hilog.info(DOMAIN_NUMBER, TAG, `onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);
350    wantParam[wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false;
351    return AbilityConstant.OnContinueResult.AGREE;
352  }
353}
354```
355
356### Supporting Cross-Device Migration Between Different Abilities in the Same Application
357Generally, the same ability is involved during a cross-device migration. However, different ability names may be configured for the same service on different device types, resulting in different abilities. To support migration in this scenario, you can configure the **continueType** flag under **abilities** in the [module.json5](../quick-start/module-configuration-file.md) file for association.
358
359The values of the **continueType** tag of the two abilities must be the same. The following is an example:
360   > **NOTE**
361   >
362   > The value of **continueType** must be unique in an application. The value is a string of a maximum of 127 bytes consisting of letters, digits, and underscores (_).
363   > The **continueType** tag is a string array. If multiple fields are configured, only the first field takes effect.
364
365```json
366   // Device A
367   {
368     "module": {
369       // ...
370       "abilities": [
371         {
372           // ...
373           "name": "Ability-deviceA",
374           "continueType": ['continueType1'], // Configuration of the continueType tag.
375         }
376       ]
377     }
378   }
379
380   // Device B
381   {
382     "module": {
383       // ...
384       "abilities": [
385         {
386           // ...
387           "name": "Ability-deviceB",
388           "continueType": ['continueType1'], // Same as the value of continueType on device A.
389         }
390       ]
391     }
392   }
393```
394
395### Quickly Starting a Target Application
396By default, the target application on the peer device is not started immediately after the migration is initiated. Instead, it is started only after the data to migrate is synchronized from the source device to the peer device. To start the target application immediately after the migration is initiated, you can add the **_ContinueQuickStart** suffix to the value of **continueType**. In this way, only the migrated data is restored after the data synchronization, delivering an even better migration experience.
397
398   ```json
399   {
400     "module": {
401       // ...
402       "abilities": [
403         {
404           // ...
405           "name": "EntryAbility"
406           "continueType": ['EntryAbility_ContinueQuickStart'], // If the continueType tag has been configured, add the suffix '_ContinueQuickStart' to its value. If the continueType tag is not configured, you can use AbilityName + '_ContinueQuickStart' as the value of continueType to implement quick startup of the target application.
407         }
408       ]
409     }
410   }
411   ```
412
413## Cross-Device Data Migration
414
415Two data migration modes are provided. You can select either of them as required.
416  > **NOTE**
417  >
418  > Through the configuration of **restoreId**, certain ArkUI components can be restored to a given state on the target device after migration. For details, see [restoreId](../../application-dev/reference/apis-arkui/arkui-ts/ts-universal-attributes-restoreId.md).
419  >
420  > If distributed data objects need to be migrated, you must perform the following operations (required only in API version 11 and earlier versions):
421  >
422  > 1. Declare the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md).
423  >
424  > 2. Display a dialog box to ask for authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/AccessToken/request-user-authorization.md).
425
426### Using wantParam for Data Migration
427
428If the size of the data to migrate is less than 100 KB, you can add fields to **wantParam** to migrate the data. An example is as follows:
429
430```ts
431import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
432import { hilog } from '@kit.PerformanceAnalysisKit';
433
434const TAG: string = '[MigrationAbility]';
435const DOMAIN_NUMBER: number = 0xFF00;
436
437export default class MigrationAbility extends UIAbility {
438  storage: LocalStorage = new LocalStorage();
439
440  // Save the data on the source device.
441  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
442    // Save the data to migrate in the custom field (for example, data) of wantParam.
443    const continueInput = 'Data to migrate';
444    wantParam['data'] = continueInput;
445    return AbilityConstant.OnContinueResult.AGREE;
446  }
447
448  // Restore the data on the target device.
449  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
450    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
451      // Obtain the data saved.
452      let continueInput = '';
453      if (want.parameters !== undefined) {
454        continueInput = JSON.stringify(want.parameters.data);
455        hilog.info(DOMAIN_NUMBER, TAG, `continue input ${continueInput}`);
456      }
457      // Trigger page restoration.
458      this.context.restoreWindowStage(this.storage);
459    }
460  }
461
462  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
463    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
464      let continueInput = '';
465      if (want.parameters !== undefined) {
466        continueInput = JSON.stringify(want.parameters.data);
467        hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);
468      }
469      // Trigger page restoration.
470      this.context.restoreWindowStage(this.storage);
471    }
472  }
473}
474```
475
476### Using Distributed Data Objects for Data Migration
477
478If the size of the data to migrate is greater than 100 KB or a file needs to be migrated, you can use a [distributed data object](../reference/apis-arkdata/js-apis-data-distributedobject.md) to implement data migration. For details, see [Cross-Device Synchronization of Distributed Data Objects](../database/data-sync-of-distributed-data-object.md).
479
480  > **NOTE**
481  >
482  > Since API version 12, it is difficult to obtain the file synchronization completion time when [cross-device file access](../file-management/file-access-across-devices.md) is used to migrate a file. To ensure a higher success rate, you are advised to use distributed data objects to carry assets. File migration implemented through cross-device file access still takes effect.
483
484#### Basic Data Migration
485
486To use a distributed data object, you must save data in the [onContinue()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncontinue) API on the source device and restore data in the [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant) API of the target device.
487
488On the source device, save the data to migrate to a distributed [data object](../reference/apis-arkdata/js-apis-data-distributedobject.md#dataobject).
489
490- Use [create()](../reference/apis-arkdata/js-apis-data-distributedobject.md#distributeddataobjectcreate9) in **onContinue()** to create a distributed data object and add the data to migrate to the object.
491- Use [genSessionId()](../reference/apis-arkdata/js-apis-data-distributedobject.md#distributeddataobjectgensessionid) to generate a data object network ID, use the ID to call [setSessionId()](../reference/apis-arkdata/js-apis-data-distributedobject.md#setsessionid9) to add the data object to the network, and activate the distributed data object.
492- Use [save()](../reference/apis-arkdata/js-apis-data-distributedobject.md#save9) to persist the activated distributed data object to ensure that the peer device can still obtain data after the application exits from the source device.
493- The generated session ID is transferred to the peer device through **want** for activation and synchronization purposes.
494
495> **NOTE**
496>
497> Distributed data objects must be activated before being made persistent. Therefore, the **save()** API must be called after setSessionId().
498> For applications that need to exit from the source device after migration, use **await** to wait until the **save()** API finishes execution. This prevents the application from exiting before data is saved. Since API version 12, an asynchronous **onContinue()** API is provided for this scenario.
499> Currently, the **sessionId** field in **wantParams** is occupied by the system in the migration process. You are advised to define another key in **wantParams** to store the ID to avoid data exceptions.
500
501The sample code is as follows:
502
503```ts
504// Import the modules.
505import { distributedDataObject } from '@kit.ArkData';
506import { UIAbility, AbilityConstant } from '@kit.AbilityKit';
507import { BusinessError } from '@kit.BasicServicesKit';
508import { hilog } from '@kit.PerformanceAnalysisKit';
509
510const TAG: string = '[MigrationAbility]';
511const DOMAIN_NUMBER: number = 0xFF00;
512
513// Define service data.
514class ParentObject {
515  mother: string
516  father: string
517
518  constructor(mother: string, father: string) {
519    this.mother = mother
520    this.father = father
521  }
522}
523
524// Strings, digits, Boolean values, and objects can be transferred.
525class SourceObject {
526  name: string | undefined
527  age: number | undefined
528  isVis: boolean | undefined
529  parent: ParentObject | undefined
530
531  constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
532    this.name = name
533    this.age = age
534    this.isVis = isVis
535    this.parent = parent
536  }
537}
538
539export default class MigrationAbility extends UIAbility {
540  d_object?: distributedDataObject.DataObject;
541
542  async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {
543    // ...
544    let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
545    let source: SourceObject = new SourceObject("jack", 18, false, parentSource);
546
547    // Create a distributed data object.
548    this.d_object = distributedDataObject.create(this.context, source);
549
550    // Generate a data object network ID and activate the distributed data object.
551    let dataSessionId: string = distributedDataObject.genSessionId();
552    this.d_object.setSessionId(dataSessionId);
553
554    // Transfer the network ID to the peer device.
555    wantParam['dataSessionId'] = dataSessionId;
556
557    // Persist the data object to ensure that the peer device can restore the data object even if the application exits after the migration.
558    // Obtain the network ID of the peer device from wantParam.targetDevice as an input parameter.
559    await this.d_object.save(wantParam.targetDevice as string).then((result:
560      distributedDataObject.SaveSuccessResponse) => {
561      hilog.info(DOMAIN_NUMBER, TAG, `Succeeded in saving. SessionId: ${result.sessionId}`,
562        `version:${result.version}, deviceId:${result.deviceId}`);
563    }).catch((err: BusinessError) => {
564      hilog.error(DOMAIN_NUMBER, TAG, 'Failed to save. Error: ', JSON.stringify(err) ?? '');
565    });
566
567    return AbilityConstant.OnContinueResult.AGREE;
568  }
569}
570```
571
572In [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant), the peer device adds the same distributed data object as the source device for networking, in order to restore data.
573
574- Create an empty distributed data object to receive restored data.
575- Read the network ID of the distributed data object from [want](../reference/apis-ability-kit/js-apis-app-ability-want.md).
576- Register [on()](../reference/apis-arkdata/js-apis-data-distributedobject.md#onstatus9) to listen for data changes. In the event callback indicating that **status** is **restored**, implement the service operations that need to be performed when the data restoration is complete.
577- Call [setSessionId()](../reference/apis-arkdata/js-apis-data-distributedobject.md#setsessionid9) to add the data object to the network, and activate the distributed data object.
578
579> **NOTE**
580>
581> 1. The distributed data object of the peer device to be added to the network cannot be a temporary variable. This is because the callback of the **on()** API may be executed after **onCreate()** or **onNewWant()** finishes execution. If the temporary variable is released, a null pointer exception may occur. You can use a class member variable to avoid this problem.
582> 2. The attributes of the object used to create the distributed data object on the peer device must be undefined before the distributed data object is activated. Otherwise, the source data will be overwritten after new data is added to the network, and data restoration will fail.
583> 3. Before activating the distributed data object, call **on()** to listen for the restore event. This helps prevent data restoration failure caused by event missing.
584
585The sample code is as follows:
586
587```ts
588import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
589import { distributedDataObject } from '@kit.ArkData';
590import { hilog } from '@kit.PerformanceAnalysisKit';
591
592const TAG: string = '[MigrationAbility]';
593const DOMAIN_NUMBER: number = 0xFF00;
594
595// The definition of the example data object is the same as above.
596export default class MigrationAbility extends UIAbility {
597  d_object?: distributedDataObject.DataObject;
598
599  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
600    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
601      // ...
602      // Call the encapsulated distributed data object processing function.
603      this.handleDistributedData(want);
604    }
605  }
606
607  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
608    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
609      if (want.parameters !== undefined) {
610        // ...
611        // Call the encapsulated distributed data object processing function.
612        this.handleDistributedData(want);
613      }
614    }
615  }
616
617  handleDistributedData(want: Want) {
618    // Create an empty distributed data object.
619    let remoteSource: SourceObject = new SourceObject(undefined, undefined, undefined, undefined);
620    this.d_object = distributedDataObject.create(this.context, remoteSource);
621
622    // Read the network ID of the distributed data object.
623    let dataSessionId = '';
624    if (want.parameters !== undefined) {
625      dataSessionId = want.parameters.dataSessionId as string;
626    }
627
628    // Add a data change listener.
629    this.d_object.on("status", (sessionId: string, networkId: string, status: 'online' | 'offline' | 'restored') => {
630      hilog.info(DOMAIN_NUMBER, TAG, "status changed " + sessionId + " " + status + " " + networkId);
631      if (status == 'restored') {
632        if (this.d_object) {
633          // Read data from the distributed data object when the restore status is received.
634          hilog.info(DOMAIN_NUMBER, TAG, "restored name:" + this.d_object['name']);
635          hilog.info(DOMAIN_NUMBER, TAG, "restored parents:" + JSON.stringify(this.d_object['parent']));
636        }
637      }
638    });
639
640    // Activate the distributed data object.
641    this.d_object.setSessionId(dataSessionId);
642  }
643}
644```
645
646#### File Migration
647
648A file, such as an image or document, must be converted to the [commonType.Asset](../reference/apis-arkdata/js-apis-data-commonType.md#asset) type before being encapsulated into a distributed data objects for migration. The migration implementation is similar to that of a common distributed data object. The following describes only the differences.
649
650On the source device, save the file asset to migrate to a distributed [data object](../reference/apis-arkdata/js-apis-data-distributedobject.md#dataobject).
651
652- Copy the file asset to the [distributed file directory](application-context-stage.md#obtaining-application-file-paths). For details about related APIs and usage, see [basic file APIs](../file-management/app-file-access.md).
653- Use the file in the distributed file directory to create an asset object.
654- Save the asset object as the root attribute of the distributed data object.
655
656Then, add the data object to the network and persist it. The procedure is the same as the migration of a common data object on the source device.
657
658An example is as follows:
659
660```ts
661// Import the modules.
662import { UIAbility, AbilityConstant } from '@kit.AbilityKit';
663import { distributedDataObject, commonType } from '@kit.ArkData';
664import { fileIo, fileUri } from '@kit.CoreFileKit';
665import { hilog } from '@kit.PerformanceAnalysisKit';
666import { BusinessError } from '@ohos.base';
667
668const TAG: string = '[MigrationAbility]';
669const DOMAIN_NUMBER: number = 0xFF00;
670
671// Define a data object.
672class ParentObject {
673  mother: string
674  father: string
675
676  constructor(mother: string, father: string) {
677    this.mother = mother
678    this.father = father
679  }
680}
681
682class SourceObject {
683  name: string | undefined
684  age: number | undefined
685  isVis: boolean | undefined
686  parent: ParentObject | undefined
687  attachment: commonType.Asset | undefined // New asset attribute.
688
689  constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined,
690              parent: ParentObject | undefined, attachment: commonType.Asset | undefined) {
691    this.name = name
692    this.age = age
693    this.isVis = isVis
694    this.parent = parent
695    this.attachment = attachment;
696  }
697}
698
699export default class MigrationAbility extends UIAbility {
700  d_object?: distributedDataObject.DataObject;
701
702  async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {
703    // ...
704
705    // 1. Write the asset to the distributed file directory.
706    let distributedDir: string = this.context.distributedFilesDir; // Obtain the distributed file directory.
707    let fileName: string = '/test.txt'; // File name.
708    let filePath: string = distributedDir + fileName; // File path.
709
710    try {
711      // Create a file in the distributed directory.
712      let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
713      hilog.info(DOMAIN_NUMBER, TAG, 'Create file success.');
714      // Write content to the file. (If the asset is an image, convert the image into a buffer and write the buffer.)
715      fileIo.writeSync(file.fd, '[Sample] Insert file content here.');
716      // Close the file.
717      fileIo.closeSync(file.fd);
718    } catch (error) {
719      let err: BusinessError = error as BusinessError;
720      hilog.error(DOMAIN_NUMBER, TAG, `Failed to openSync / writeSync / closeSync. Code: ${err.code}, message: ${err.message}`);
721    }
722
723    // 2. Use the file in the distributed file directory to create an asset object.
724    let distributedUri: string = fileUri.getUriFromPath(filePath); // Obtain the URI of the distributed file.
725
726    // Obtain file parameters.
727    let ctime: string = '';
728    let mtime: string = '';
729    let size: string = '';
730    await fileIo.stat(filePath).then((stat: fileIo.Stat) => {
731      ctime = stat.ctime.toString(); // Creation time
732      mtime = stat.mtime.toString(); // Modification time
733      size = stat.size.toString(); // File size
734    })
735
736    // Create an asset object.
737    let attachment: commonType.Asset = {
738      name: fileName,
739      uri: distributedUri,
740      path: filePath,
741      createTime: ctime,
742      modifyTime: mtime,
743      size: size,
744    }
745
746    // 3. Use the asset object as the root attribute of the distributed data object to create a distributed data object.
747    let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
748    let source: SourceObject = new SourceObject("jack", 18, false, parentSource, attachment);
749    this.d_object = distributedDataObject.create(this.context, source);
750
751    // Generate a network ID, activate the distributed data object, and save the data persistently.
752    // ...
753
754    return AbilityConstant.OnContinueResult.AGREE;
755  }
756}
757```
758
759The target application must create an asset object whose attributes are empty as the root attribute of the distributed data object. When the [on()](../reference/apis-arkdata/js-apis-data-distributedobject.md#onstatus9) event callback in which **status** is **restored** is received, the data synchronization including the asset is complete. The asset object of source application can be obtained in the same way as the basic data.
760
761> **NOTE**
762>
763> When the target application creates the distributed data object, the assets in the **SourceObject** object cannot be directly initialized using **undefined**. You need to create an asset object whose initial values of all attributes are empty so that the distributed object can identify the asset type.
764
765```ts
766import { UIAbility, Want } from '@kit.AbilityKit';
767import { distributedDataObject, commonType } from '@kit.ArkData';
768import { hilog } from '@kit.PerformanceAnalysisKit';
769
770const TAG: string = '[MigrationAbility]';
771const DOMAIN_NUMBER: number = 0xFF00;
772
773export default class MigrationAbility extends UIAbility {
774  d_object?: distributedDataObject.DataObject;
775
776  handleDistributedData(want: Want) {
777    // ...
778    // Create an asset object whose attributes are empty.
779    let attachment: commonType.Asset = {
780      name: '',
781      uri: '',
782      path: '',
783      createTime: '',
784      modifyTime: '',
785      size: '',
786    }
787
788    // Use the empty asset object to create a distributed data object. Other basic attributes can be directly set to undefined.
789    let source: SourceObject = new SourceObject(undefined, undefined, undefined, undefined, attachment);
790    this.d_object = distributedDataObject.create(this.context, source);
791
792    this.d_object.on("status", (sessionId: string, networkId: string, status: 'online' | 'offline' | 'restored') => {
793      if (status == 'restored') {
794        if (this.d_object) {
795          // The restored event callback is received, indicating that the synchronization of the distributed asset object is complete.
796          hilog.info(DOMAIN_NUMBER, TAG, "restored attachment:" + JSON.stringify(this.d_object['attachment']));
797        }
798      }
799    });
800    // ...
801  }
802}
803```
804
805If you want to synchronize multiple assets, use either of the following methods:
806
807- Method 1: Implement each asset as a root attribute of the distributed data object. This applies to scenarios where the number of assets to migrate is fixed.
808- Method 2: Transfer the asset array as an object. This applies to scenarios where the number of assets to migrate changes dynamically (for example, a user selects a variable number of images). Currently, the asset array cannot be directly transferred as the root attribute.
809
810To implement method 1, you can add more assets by referring to the method of adding an asset. To implement method 2, refer to the code snippet below:
811
812```ts
813// Import the modules.
814import { distributedDataObject, commonType } from '@kit.ArkData';
815import { UIAbility } from '@kit.AbilityKit';
816
817// Define a data object.
818class SourceObject {
819  name: string | undefined
820  assets: Object | undefined // Add an object attribute to the distributed data object.
821
822  constructor(name: string | undefined, assets: Object | undefined) {
823    this.name = name
824    this.assets = assets;
825  }
826}
827
828export default class MigrationAbility extends UIAbility {
829  d_object?: distributedDataObject.DataObject;
830
831  // This API is used to convert an asset array into a record.
832  GetAssetsWapper(assets: commonType.Assets): Record<string, commonType.Asset> {
833    let wrapper: Record<string, commonType.Asset> = {}
834    let num: number = assets.length;
835    for (let i: number = 0; i < num; i++) {
836      wrapper[`asset${i}`] = assets[i];
837    }
838    return wrapper;
839  }
840
841  async onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
842    // ...
843
844    // Create multiple asset objects.
845    let attachment1: commonType.Asset = {
846      // ...
847    }
848
849    let attachment2: commonType.Asset = {
850      // ...
851    }
852
853    // Insert the asset objects into the asset array.
854    let assets: commonType.Assets = [];
855    assets.push(attachment1);
856    assets.push(attachment2);
857
858    // Convert the asset array into a record object and use it to create a distributed data object.
859    let assetsWrapper: Object = this.GetAssetsWapper(assets);
860    let source: SourceObject = new SourceObject("jack", assetsWrapper);
861    this.d_object = distributedDataObject.create(this.context, source);
862
863    // ...
864  }
865}
866```
867
868## Verification Guide
869
870A mission center demo is provided for you to verify the migration capability of your application. The following walks you through on how to verify migration by using the demo.
871
872> **NOTE**
873>
874> The screenshots in this section are for reference only. The DevEco Studio and SDK versions in use prevail.
875
876**Compiling and Installing the Demo**
877
8781. [Switch to the full SDK](../faqs/full-sdk-switch-guide.md) to compile and install the mission center.
879
8802. Download the sample code of the [mission center demo](https://gitee.com/openharmony/ability_dmsfwk/tree/master/services/dtbschedmgr/test/missionCenterDemo/dmsDemo/entry/src/main).
881
8823. Build the project file.
883
884    1. Create an empty project and replace the corresponding folders with the downloaded files.
885
886        ![hop-cross-device-migration](figures/hop-cross-device-migration1.png)
887
888    2. Complete the signature, build, and installation.
889        ​The default signature permission provided by the automatic signature template of DevEco Studio is normal. The mission center demo requires the **ohos.permission.MANAGE_MISSIONS** permission, which is at the system_core level. Therefore, you must escalate the permission to the system_core level.
890           1. Change **"apl":"normal"** to **"apl":"system_core"** in the **UnsignedReleasedProfileTemplate.json** file in **openharmony\apiVersion\toolchains\lib**, where apiVersion is a digit, for example, **10**. 
891
892           2. Choose **File > Project Structure**.
893
894           ![hop-cross-device-migration](figures/hop-cross-device-migration2.png)
895
896           3. Click **Signing Configs** and click **OK**.
897
898           ![hop-cross-device-migration](figures/hop-cross-device-migration3.png)
899    
900           4. Connect to the developer board and run the demo.
901
902**Device Networking**
903
9041. Open the calculators of devices A and B.
9052. Click the arrow in the upper right corner to select device B.
9063. Select a trusted device on device B. The PIN is displayed.
9074. Enter the PIN on device A.
9085. Verify the networking. Enter a number on device A. If the number is displayed on device B, the networking is successful.
909
910**Initiation Migration**
911
9121. Open your application on device B, and open the mission center demo on device A. The name of device A and the name of device B are displayed on the mission center demo.
9132. Touch the name of device B. The application card list of device B is displayed.
9143. Drag the application card to be connected to the name of device A. The application on device A is started.
915
916## FAQs
917
918### Q1: What Can I Do If the Application Cannot Be Migrated Back to the Source Device?
919
920The migration state is set at the ability level. The application on the target device may have executed the command to set its own migration state (for example, set the state to **INACTIVE** in [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityoncreate) during cold start or to **INACTIVE** during hot start since the application has opened a page that cannot be migrated). To ensure a migration back to the source device, you must set the migration state to **ACTIVE** in onCreate() or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonnewwant).
921
922```ts
923// MigrationAbility.ets
924import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
925import { hilog } from '@kit.PerformanceAnalysisKit';
926
927const TAG: string = '[MigrationAbility]';
928const DOMAIN_NUMBER: number = 0xFF00;
929
930export default class MigrationAbility extends UIAbility {
931  // ...
932  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
933    // ...
934    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
935      // ...
936      // Set the migration state to ACTIVE when the launch is caused by migration. This setting copes with cold start.
937      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
938        hilog.info(DOMAIN_NUMBER, TAG, `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
939      });
940    }
941    // ...
942  }
943
944  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
945    // ...
946    // Set the migration state to ACTIVE when the launch is caused by migration. This setting copes with hot start.
947    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
948      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
949        hilog.info(DOMAIN_NUMBER, TAG, `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
950      });
951    }
952  }
953  // ...
954}
955```
956
957### Q2: What Can I Do If the Call of loadContent() Does Not Take Effect in onWindowStageRestore()?
958
959If page stack migration is not disabled for an application, the system migrates and loads the page stack of the application by default. In this case, if you use [loadContent()](../reference/apis-arkui/js-apis-window.md#loadcontent9) to trigger the loading of a specific page in the [onWindowStageRestore()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagerestore) lifecycle callback function, the loading does not take effect and the page in the page stack is still restored.
960
961