• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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#setmissioncontinuestate10) 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#params) 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#params) 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#oncontinue) 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#oncreate) callback (in the case of cold start) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onnewwant) 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
47For better user experience, the data to be transmitted via the **wantParam** parameter must be less than 100 KB.
48
49## How to Develop
50
511. Configure the **continuable** tag under **abilities** in the [module.json5 file](../quick-start/module-configuration-file.md).
52
53   ```json
54   {
55     "module": {
56       // ...
57       "abilities": [
58         {
59           // ...
60           "continuable": true, // Configure the UIAbility to support migration.
61         }
62       ]
63     }
64   }
65   ```
66
67   > **NOTE**
68   >
69   > Configure the application launch type. For details, see [UIAbility Launch Type](uiability-launch-type.md).
70
712. Implement **onContinue()** in the UIAbility on the source device.
72
73    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#oncontinue) 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.
74
75    1. Saving data to migrate: You can save the data to migrate in key-value pairs in **wantParam**.
76
77    2. (Optional) 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. If the version compatibility check fails, the application should notify users of the cause of the migration failure.
78
79       > **NOTE**
80       >
81       > If the compatibility issues have little or no impact on migration experience, you can skip this check.
82
83    3. Returning the migration result: 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#oncontinueresult).
84
85     
86    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.
87
88    | Field|Description|
89    | ---- | ---- |
90    | version | Version the application on the target device.|
91    | targetDevice | Network ID of the target device.|
92
93    ```ts
94    import { AbilityConstant, UIAbility } from '@kit.AbilityKit';
95    import { hilog } from '@kit.PerformanceAnalysisKit';
96    import { promptAction } from '@kit.ArkUI';
97
98    const TAG: string = '[MigrationAbility]';
99    const DOMAIN_NUMBER: number = 0xFF00;
100
101    export default class MigrationAbility extends UIAbility {
102      // Prepare data to migrate in onContinue.
103      onContinue(wantParam: Record<string, Object>):AbilityConstant.OnContinueResult {
104        let targetVersion = wantParam.version;
105        let targetDevice = wantParam.targetDevice;
106        hilog.info(DOMAIN_NUMBER, TAG, `onContinue version = ${targetVersion}, targetDevice: ${targetDevice}`);
107
108        // The application can set the minimum compatible version based on the source version, which can be obtained from the versionCode field in the app.json5 file. This is to prevent incompatibility caused because the target version is too earlier.
109        let versionThreshold: number = -1; // Use the minimum version supported by the application.
110        // Compatibility verification
111        if (targetVersion < versionThreshold) {
112          // It is recommended that users be notified of the reason why the migration is rejected if the version compatibility check fails.
113          promptAction.openToast({
114              message: 'The target application version is too early to continue. Update the application and try again.',
115              duration: 2000
116          })
117          // Return MISMATCH when the compatibility check fails.
118          return AbilityConstant.OnContinueResult.MISMATCH;
119        }
120
121        // Save the data to migrate in the custom field (for example, data) of wantParam.
122        const continueInput = 'Data to migrate';
123        wantParam['data'] = continueInput;
124
125        return AbilityConstant.OnContinueResult.AGREE;
126      }
127    }
128    ```
129
1303. For the UIAbility on the target device, implement [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#oncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onnewwant) to restore the data and load the UI.
131
132    The APIs to call vary according to the launch types, as shown below.
133
134    ![hop-cross-device-migration](figures/hop-cross-device-migration5.png)
135
136    > **NOTE**
137    >
138    > 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.
139    >
140    > 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.
141
142    - The **launchReason** parameter in the **onCreate()** or **onNewWant()** callback specifies whether the launch is triggered as a result of a migration (whether the value is **CONTINUATION**).
143    - You can obtain the saved data from the [want](../reference/apis-ability-kit/js-apis-app-ability-want.md) parameter.
144    - To use system-level page stack restoration, you must call [restoreWindowStage()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#restorewindowstage) 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#migrating-the-page-stack-on-demand).
145
146    ```ts
147    import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
148    import { hilog } from '@kit.PerformanceAnalysisKit';
149
150    const TAG: string = '[MigrationAbility]';
151    const DOMAIN_NUMBER: number = 0xFF00;
152
153    export default class MigrationAbility extends UIAbility {
154      storage : LocalStorage = new LocalStorage();
155
156      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
157        hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onCreate');
158        if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
159          // Restore the saved data from want.parameters.
160          let continueInput = '';
161          if (want.parameters !== undefined) {
162            continueInput = JSON.stringify(want.parameters.data);
163            hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);
164          }
165          // Trigger page restoration.
166          this.context.restoreWindowStage(this.storage);
167        }
168      }
169
170      onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
171        hilog.info(DOMAIN_NUMBER, TAG, 'onNewWant');
172        if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
173          // Restore the saved data from want.parameters.
174          let continueInput = '';
175          if (want.parameters !== undefined) {
176            continueInput = JSON.stringify(want.parameters.data);
177            hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);
178          }
179          // Trigger page restoration.
180          this.context.restoreWindowStage(this.storage);
181        }
182      }
183    }
184    ```
185
186## Configuring Optional Migration Features
187
188### Dynamically Setting the Migration State
189
190Starting from 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#setmissioncontinuestate10). By default, **ACTIVE** is set for an application, indicating that migration is enabled.
191
192**Time for Setting the Migration State**
193
194To implement special scenarios, for example, where migration is required only for a specific page or upon a specific event, perform the following steps:
195
1961. In the [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#oncreate) lifecycle callback of the [UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md), disable cross-device migration.
197
198    ```ts
199    // MigrationAbility.ets
200    import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
201    import { hilog } from '@kit.PerformanceAnalysisKit';
202
203    const TAG: string = '[MigrationAbility]';
204    const DOMAIN_NUMBER: number = 0xFF00;
205
206    export default class MigrationAbility extends UIAbility {
207      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
208        // ...
209        this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
210          hilog.info(DOMAIN_NUMBER, TAG, `setMissionContinueState INACTIVE result: ${JSON.stringify(result)}`);
211        });
212        // ...
213      }
214    }
215    ```
216
217 2. 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.
218
219    ```ts
220    // Page_MigrationAbilityFirst.ets
221    import { AbilityConstant, common } from '@kit.AbilityKit';
222    import { hilog } from '@kit.PerformanceAnalysisKit';
223
224    const TAG: string = '[MigrationAbility]';
225    const DOMAIN_NUMBER: number = 0xFF00;
226
227    @Entry
228    @Component
229    struct Page_MigrationAbilityFirst {
230      private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
231
232      // ...
233      onPageShow(){
234        // When the page is displayed, set the migration state to ACTIVE.
235        this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
236          hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
237        });
238      }
239
240      build() {
241        // ...
242      }
243    }
244    ```
245
2463. 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:
247
248    ```ts
249    // Page_MigrationAbilityFirst.ets
250    import { AbilityConstant, common } from '@kit.AbilityKit';
251    import { hilog } from '@kit.PerformanceAnalysisKit';
252    import { promptAction } from '@kit.ArkUI';
253
254    const TAG: string = '[MigrationAbility]';
255    const DOMAIN_NUMBER: number = 0xFF00;
256
257    @Entry
258    @Component
259    struct Page_MigrationAbilityFirst {
260      private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
261      build() {
262        Column() {
263          //...
264          List({ initialIndex: 0 }) {
265            ListItem() {
266              Row() {
267                //...
268              }
269              .onClick(() => {
270                // When the button is clicked, set the migration state to ACTIVE.
271                this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
272                  hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
273                  promptAction.openToast({
274                    message: 'Success'
275                  });
276                });
277              })
278            }
279            //...
280          }
281          //...
282        }
283        //...
284      }
285    }
286    ```
287
288### Migrating the Page Stack on Demand
289
290
291> **NOTE**
292>
293> 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.
294>
295> 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#params) 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.
296
297By 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#oncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onnewwant) finishes the execution, call [restoreWindowStage()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#restorewindowstage) 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.
298
299The following uses **onCreate()** as an example:
300
301```ts
302import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
303
304export default class MigrationAbility extends UIAbility {
305  storage : LocalStorage = new LocalStorage();
306
307  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
308      // ...
309      // Trigger page restoration before the synchronous API finishes the execution.
310      this.context.restoreWindowStage(this.storage);
311  }
312}
313```
314
315If 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#params) to **false**, and specify the page to be displayed after the migration in [onWindowStageRestore()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onwindowstagerestore). If the page to be displayed after the migration is not specified, a blank page will be displayed.
316
317For 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.
318
319```ts
320// MigrationAbility.ets
321import { AbilityConstant, UIAbility, wantConstant } from '@kit.AbilityKit';
322import { hilog } from '@kit.PerformanceAnalysisKit';
323import { window } from '@kit.ArkUI';
324
325const TAG: string = '[MigrationAbility]';
326const DOMAIN_NUMBER: number = 0xFF00;
327
328export default class MigrationAbility extends UIAbility {
329  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
330    // ...
331    // Configure not to use the system page stack during restoration.
332    wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;
333    return AbilityConstant.OnContinueResult.AGREE;
334  }
335
336  onWindowStageRestore(windowStage: window.WindowStage): void {
337    // If the system page stack is not used for restoration, specify the page to be displayed after the migration.
338    windowStage.loadContent('pages/page_migrationability/Page_MigrationAbilityThird', (err, data) => {
339      if (err.code) {
340        hilog.error(DOMAIN_NUMBER, TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
341        return;
342      }
343    });
344  }
345}
346```
347
348### Exiting the Application on Demand
349
350By 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#params) to **false**.
351
352Example: A UIAbility on the source device does not exit after a successful migration.
353
354```ts
355import { AbilityConstant, UIAbility, wantConstant } from '@kit.AbilityKit';
356import { hilog } from '@kit.PerformanceAnalysisKit';
357
358const TAG: string = '[MigrationAbility]';
359const DOMAIN_NUMBER: number = 0xFF00;
360
361export default class MigrationAbility extends UIAbility {
362  // ...
363  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
364    hilog.info(DOMAIN_NUMBER, TAG, `onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);
365    wantParam[wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false;
366    return AbilityConstant.OnContinueResult.AGREE;
367  }
368}
369```
370
371### Migrating Between Abilities in the Same Application Across Devices
372Generally, 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.
373
374The values of the **continueType** tag of the two abilities must be the same. The following is an example:
375   > **NOTE**
376   >
377   > 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 (_).
378   >
379   > The **continueType** tag is a string array. If multiple fields are configured, only the first field takes effect.
380
381```json
382   // Device A
383   {
384     "module": {
385       // ...
386       "abilities": [
387         {
388           // ...
389           "name": "Ability-deviceA",
390           "continueType": ['continueType1'], // Configuration of the continueType tag.
391         }
392       ]
393     }
394   }
395
396   // Device B
397   {
398     "module": {
399       // ...
400       "abilities": [
401         {
402           // ...
403           "name": "Ability-deviceB",
404           "continueType": ['continueType1'], // Same as the value of continueType on device A.
405         }
406       ]
407     }
408   }
409```
410
411### Migrating Abilities with Different Bundle Names in the Same Application Across Devices
412An application may use different bundle names on different devices. To support migration in this scenario, configure **abilities** in the **module.json5** file of the application as follows:
413
414- **continueBundleName**: bundle name of the application on the peer device.
415- **continueType**: The same value must be used.
416
417   > **NOTE**
418   >
419   > 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 (_).
420   >
421   > The **continueType** tag is a string array. If multiple fields are configured, only the first field takes effect.
422
423
424An example is as follows:
425
426An application with different bundle names is migrated between device A and device B. The bundle name of the application on device A is com.demo.example1, and that on device B is com.demo.example2.
427
428```JSON
429// In the configuration file for device A, set continueBundleName to the bundle name of the application on device B.
430{
431  "module": {
432    // ···
433    "abilities": [
434      {
435        "name": "EntryAbility",
436        // ···
437        "continueType": ["continueType"],
438        "continueBundleName": ["com.demo.example2"], // continueBundleName is set to com.demo.example2, which is the bundle name of the application on device B.
439
440      }
441    ]
442
443  }
444}
445```
446
447```JSON
448// In the configuration file for device B, set continueBundleName to the bundle name of the application on device A.
449{
450  "module": {
451    // ···
452    "abilities": [
453      {
454        "name": "EntryAbility",
455        // ···
456        "continueType": ["continueType"],
457        "continueBundleName": ["com.demo.example1"], // continueBundleName is set to com.demo.example1, which is the bundle name of the application on device A.
458
459      }
460    ]
461
462  }
463}
464
465```
466
467### Quickly Starting a Target Application
468By default, the target application on the peer device is not started immediately when the migration is initiated. It waits until the data to migrate is synchronized from the source device to the peer device. To start the target application immediately upon the initiation of a migration, 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.
469
470   ```json
471   {
472     "module": {
473       // ...
474       "abilities": [
475         {
476           // ...
477           "name": "EntryAbility"
478           "continueType": ['EntryAbility_ContinueQuickStart'], // If the continueType tag is 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.
479         }
480       ]
481     }
482   }
483   ```
484With quick start, the target application starts while waiting for the data to migration, minimizing the duration that users wait for the migration to complete. Note that, for the first migration with quick start enabled, the [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#oncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onnewwant) callback is triggered, in which **launchReason** is **PREPARE_CONTINUATION**. The introduction of the **launchReason** parameter solves problems related to redirection and timing. It also provides a loading screen during quick startup.
485
486Starting from API version 18, an application that displays a loading page during quick launch can [obtain the quick startup result during cross-device migration](../reference/apis-ability-kit/js-apis-app-ability-continueManager.md#continuemanageron). Depending on this result, the application can take appropriate actions. For example, if the quick startup is successful, the application can dismiss the loading page and proceed to the continuation page.
487
488The following figure shows the quick start process.
489
490![hop-cross-device-migration](figures/continue_quick_start.png)
491
492For an application configured with quick startup, two start requests are received for a migration. The differences are as follows:
493
494| Scenario          | Lifecycle Function                               | launchParam.launchReason                          |
495| -------------- | ------------------------------------------- | ------------------------------------------------- |
496| First start request| onCreate (in the case of cold start)<br>or onNewWant (in the case of hot start)| AbilityConstant.LaunchReason.PREPARE_CONTINUATION |
497| Second start request| onNewWant                                   | AbilityConstant.LaunchReason.CONTINUATION         |
498
499If fast start is not configured, only one start request is received.
500
501| Scenario        | Lifecycle Function                               | launchParam.launchReason                  |
502| ------------ | ------------------------------------------- | ----------------------------------------- |
503| One start request| onCreate (in the case of cold start)<br>or onNewWant (in the case of hot start)| AbilityConstant.LaunchReason.CONTINUATION |
504
505When quick start is configured, you also need to implement [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#oncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onnewwant). The following is an example:
506
507```ts
508import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
509import { hilog } from '@kit.PerformanceAnalysisKit';
510
511const TAG: string = '[MigrationAbility]';
512const DOMAIN_NUMBER: number = 0xFF00;
513
514export default class MigrationAbility extends UIAbility {
515  storage : LocalStorage = new LocalStorage();
516
517  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
518    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onCreate');
519
520    // 1. Quick start is configured. Trigger the lifecycle callback when the application is launched immediately.
521    if (launchParam.launchReason === AbilityConstant.LaunchReason.PREPARE_CONTINUATION) {
522      // If the application data to migrate is large, add a loading screen here (for example, displaying "loading" on the screen).
523      // Handle issues related to custom redirection and timing.
524      // ...
525    }
526  }
527
528  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
529    hilog.info(DOMAIN_NUMBER, TAG, 'onNewWant');
530
531    // 1. Quick start is configured. Trigger the lifecycle callback when the application is launched immediately.
532    if (launchParam.launchReason === AbilityConstant.LaunchReason.PREPARE_CONTINUATION) {
533      // If the application data to migrate is large, add a loading screen here (for example, displaying "loading" on the screen).
534      // Handle issues related to custom redirection and timing.
535      // ...
536    }
537
538    // 2. Trigger the lifecycle callback when the migration data is restored.
539    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
540      // Restore the saved data from want.parameters.
541      let continueInput = '';
542      if (want.parameters !== undefined) {
543        continueInput = JSON.stringify(want.parameters.data);
544        hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);
545      }
546      // Trigger page restoration.
547      this.context.restoreWindowStage(this.storage);
548    }
549  }
550}
551```
552
553When the target application is quickly started, the [onWindowStageCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onwindowstagecreate) and [onWindowStageRestore()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onwindowstagerestore) callbacks are triggered in sequence. Generally, in **onWindowStageCreate()**, you call [loadContent()](../reference/apis-arkui/arkts-apis-window-Window.md#loadcontent9) to load the page. This API throws an asynchronous task to load the home page. This asynchronous task is not synchronous with **onWindowStageRestore()**. If UI-related APIs (such as route APIs) are used in **onWindowStageRestore()**, the invoking time may be earlier than the home page loading time. To ensure the normal loading sequence, you can use [setTimeout()](../reference/common/js-apis-timer.md#settimeout) to throw an asynchronous task for related operations.
554
555The sample code is as follows:
556
557```ts
558import { UIAbility } from '@kit.AbilityKit';
559import { hilog } from '@kit.PerformanceAnalysisKit';
560import { UIContext, window } from '@kit.ArkUI';
561
562export default class EntryAbility extends UIAbility {
563  private uiContext: UIContext | undefined = undefined;
564
565  // ...
566
567  onWindowStageCreate(windowStage: window.WindowStage): void {
568    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
569
570    windowStage.loadContent('pages/Index', (err) => {
571      if (err.code) {
572        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
573        return;
574      }
575      this.uiContext = windowStage.getMainWindowSync().getUIContext();
576      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
577    });
578  }
579
580  onWindowStageRestore(windowStage: window.WindowStage): void {
581    setTimeout(() => {
582      // Throw the asynchronous task execution route to ensure that the task is executed after the home page is loaded.
583      this.uiContext?.getRouter().pushUrl({
584        url: 'pages/examplePage'
585      });
586    }, 0);
587  }
588
589  // ...
590}
591```
592
593## Cross-Device Data Migration
594
595Two data migration modes are provided. You can select either of them as required.
596  > **NOTE**
597  >
598  > 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).
599  >
600  > If distributed data objects need to be migrated, you must perform the following operations (required only in API version 11 and earlier versions):
601  >
602  > 1. Declare the ohos.permission.DISTRIBUTED_DATASYNC permission. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md).
603  >
604  > 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).
605
606### Using wantParam for Data Migration
607
608If 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:
609
610```ts
611import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
612import { hilog } from '@kit.PerformanceAnalysisKit';
613
614const TAG: string = '[MigrationAbility]';
615const DOMAIN_NUMBER: number = 0xFF00;
616
617export default class MigrationAbility extends UIAbility {
618  storage: LocalStorage = new LocalStorage();
619
620  // Save the data on the source device.
621  onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
622    // Save the data to migrate in the custom field (for example, data) of wantParam.
623    const continueInput = 'Data to migrate';
624    wantParam['data'] = continueInput;
625    return AbilityConstant.OnContinueResult.AGREE;
626  }
627
628  // Restore the data on the target device.
629  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
630    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
631      // Obtain the data saved.
632      let continueInput = '';
633      if (want.parameters !== undefined) {
634        continueInput = JSON.stringify(want.parameters.data);
635        hilog.info(DOMAIN_NUMBER, TAG, `continue input ${continueInput}`);
636      }
637      // Trigger page restoration.
638      this.context.restoreWindowStage(this.storage);
639    }
640  }
641
642  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
643    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
644      let continueInput = '';
645      if (want.parameters !== undefined) {
646        continueInput = JSON.stringify(want.parameters.data);
647        hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);
648      }
649      // Trigger page restoration.
650      this.context.restoreWindowStage(this.storage);
651    }
652  }
653}
654```
655
656### Using Distributed Data Objects for Data Migration
657
658If 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).
659
660  > **NOTE**
661  >
662  > Starting from 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.
663
664#### Basic Data Migration
665
666To use a distributed data object, you must save data in the [onContinue()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#oncontinue) API on the source device and restore data in the [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#oncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onnewwant) API of the target device.
667
668On the source device, save the data to migrate to a distributed [data object](../reference/apis-arkdata/js-apis-data-distributedobject.md#dataobject).
669
670- 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.
671- 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.
672- 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.
673- The generated session ID is transferred to the peer device through **want** for activation and synchronization purposes.
674
675> **NOTE**
676>
677> Distributed data objects must be activated before being made persistent. Therefore, the **save()** API must be called after **setSessionId()**.
678>
679> 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. Starting from API version 12, an asynchronous **onContinue()** API is provided for this scenario.
680>
681> 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.
682
683The sample code is as follows:
684
685```ts
686// Import the modules.
687import { distributedDataObject } from '@kit.ArkData';
688import { UIAbility, AbilityConstant } from '@kit.AbilityKit';
689import { BusinessError } from '@kit.BasicServicesKit';
690import { hilog } from '@kit.PerformanceAnalysisKit';
691
692const TAG: string = '[MigrationAbility]';
693const DOMAIN_NUMBER: number = 0xFF00;
694
695// Define service data.
696class ParentObject {
697  mother: string
698  father: string
699
700  constructor(mother: string, father: string) {
701    this.mother = mother
702    this.father = father
703  }
704}
705
706// Strings, digits, Boolean values, and objects can be transferred.
707class SourceObject {
708  name: string | undefined
709  age: number | undefined
710  isVis: boolean | undefined
711  parent: ParentObject | undefined
712
713  constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
714    this.name = name
715    this.age = age
716    this.isVis = isVis
717    this.parent = parent
718  }
719}
720
721export default class MigrationAbility extends UIAbility {
722  d_object?: distributedDataObject.DataObject;
723
724  async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {
725    // ...
726    let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
727    let source: SourceObject = new SourceObject("jack", 18, false, parentSource);
728
729    // Create a distributed data object.
730    this.d_object = distributedDataObject.create(this.context, source);
731
732    // Generate a data object network ID and activate the distributed data object.
733    let dataSessionId: string = distributedDataObject.genSessionId();
734    this.d_object.setSessionId(dataSessionId);
735
736    // Transfer the network ID to the peer device.
737    wantParam['dataSessionId'] = dataSessionId;
738
739    // Persist the data object to ensure that the peer device can restore the data object even if the application exits after the migration.
740    // Obtain the network ID of the peer device from wantParam.targetDevice as an input parameter.
741    await this.d_object.save(wantParam.targetDevice as string).then((result:
742      distributedDataObject.SaveSuccessResponse) => {
743      hilog.info(DOMAIN_NUMBER, TAG, `Succeeded in saving. SessionId: ${result.sessionId}`,
744        `version:${result.version}, deviceId:${result.deviceId}`);
745    }).catch((err: BusinessError) => {
746      hilog.error(DOMAIN_NUMBER, TAG, 'Failed to save. Error: ', JSON.stringify(err) ?? '');
747    });
748
749    return AbilityConstant.OnContinueResult.AGREE;
750  }
751}
752```
753
754In [onCreate()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#oncreate) or [onNewWant()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onnewwant), the peer device adds the same distributed data object as the source device for networking, in order to restore data.
755
756- Create an empty distributed data object to receive restored data.
757- Read the network ID of the distributed data object from [want](../reference/apis-ability-kit/js-apis-app-ability-want.md).
758- Register [on()](../reference/apis-arkdata/js-apis-data-distributedobject.md#onstatus9) to listen for data changes. In the event callback indicating that **status** is **restore**, implement the service operations that need to be performed when the data restoration is complete.
759- 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.
760
761> **NOTE**
762>
763> 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.
764>
765> 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.
766>
767> Before activating the distributed data object, call **on()** to listen for the restore event. This helps prevent data restoration failure caused by event missing.
768
769The sample code is as follows:
770
771```ts
772import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
773import { distributedDataObject } from '@kit.ArkData';
774import { hilog } from '@kit.PerformanceAnalysisKit';
775
776const TAG: string = '[MigrationAbility]';
777const DOMAIN_NUMBER: number = 0xFF00;
778
779// The definition of the example data object is the same as above.
780export default class MigrationAbility extends UIAbility {
781  d_object?: distributedDataObject.DataObject;
782
783  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
784    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
785      // ...
786      // Call the encapsulated distributed data object processing function.
787      this.handleDistributedData(want);
788    }
789  }
790
791  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
792    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
793      if (want.parameters !== undefined) {
794        // ...
795        // Call the encapsulated distributed data object processing function.
796        this.handleDistributedData(want);
797      }
798    }
799  }
800
801  handleDistributedData(want: Want) {
802    // Create an empty distributed data object.
803    let remoteSource: SourceObject = new SourceObject(undefined, undefined, undefined, undefined);
804    this.d_object = distributedDataObject.create(this.context, remoteSource);
805
806    // Read the network ID of the distributed data object.
807    let dataSessionId = '';
808    if (want.parameters !== undefined) {
809      dataSessionId = want.parameters.dataSessionId as string;
810    }
811
812    // Add a data change listener.
813    this.d_object.on("status", (sessionId: string, networkId: string, status: 'online' | 'offline' | 'restored') => {
814      hilog.info(DOMAIN_NUMBER, TAG, "status changed " + sessionId + " " + status + " " + networkId);
815      if (status == 'restored') {
816        if (this.d_object) {
817          // Read data from the distributed data object when the restore status is received.
818          hilog.info(DOMAIN_NUMBER, TAG, "restored name:" + this.d_object['name']);
819          hilog.info(DOMAIN_NUMBER, TAG, "restored parents:" + JSON.stringify(this.d_object['parent']));
820        }
821      }
822    });
823
824    // Activate the distributed data object.
825    this.d_object.setSessionId(dataSessionId);
826  }
827}
828```
829
830#### File Migration
831
832A 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.
833
834On the source device, save the file asset to migrate to a distributed [data object](../reference/apis-arkdata/js-apis-data-distributedobject.md#dataobject).
835
836- 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).
837- Use the file in the distributed file directory to create an asset object.
838- Save the asset object as the root attribute of the distributed data object.
839
840Then, 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.
841
842An example is as follows:
843
844```ts
845// Import the modules.
846import { UIAbility, AbilityConstant } from '@kit.AbilityKit';
847import { distributedDataObject, commonType } from '@kit.ArkData';
848import { fileIo, fileUri } from '@kit.CoreFileKit';
849import { hilog } from '@kit.PerformanceAnalysisKit';
850import { BusinessError } from '@ohos.base';
851
852const TAG: string = '[MigrationAbility]';
853const DOMAIN_NUMBER: number = 0xFF00;
854
855// Define a data object.
856class ParentObject {
857  mother: string
858  father: string
859
860  constructor(mother: string, father: string) {
861    this.mother = mother
862    this.father = father
863  }
864}
865
866class SourceObject {
867  name: string | undefined
868  age: number | undefined
869  isVis: boolean | undefined
870  parent: ParentObject | undefined
871  attachment: commonType.Asset | undefined // New asset attribute.
872
873  constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined,
874              parent: ParentObject | undefined, attachment: commonType.Asset | undefined) {
875    this.name = name
876    this.age = age
877    this.isVis = isVis
878    this.parent = parent
879    this.attachment = attachment;
880  }
881}
882
883export default class MigrationAbility extends UIAbility {
884  d_object?: distributedDataObject.DataObject;
885
886  async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {
887    // ...
888
889    // 1. Write the asset to the distributed file directory.
890    let distributedDir: string = this.context.distributedFilesDir; // Obtain the distributed file directory.
891    let fileName: string = '/test.txt'; // File name.
892    let filePath: string = distributedDir + fileName; // File path.
893
894    try {
895      // Create a file in the distributed directory.
896      let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
897      hilog.info(DOMAIN_NUMBER, TAG, 'Create file success.');
898      // Write content to the file. (If the asset is an image, convert the image into a buffer and write the buffer.)
899      fileIo.writeSync(file.fd, '[Sample] Insert file content here.');
900      // Close the file.
901      fileIo.closeSync(file.fd);
902    } catch (error) {
903      let err: BusinessError = error as BusinessError;
904      hilog.error(DOMAIN_NUMBER, TAG, `Failed to openSync / writeSync / closeSync. Code: ${err.code}, message: ${err.message}`);
905    }
906
907    // 2. Use the file in the distributed file directory to create an asset object.
908    let distributedUri: string = fileUri.getUriFromPath(filePath); // Obtain the URI of the distributed file.
909
910    // Obtain file parameters.
911    let ctime: string = '';
912    let mtime: string = '';
913    let size: string = '';
914    await fileIo.stat(filePath).then((stat: fileIo.Stat) => {
915      ctime = stat.ctime.toString(); // Creation time
916      mtime = stat.mtime.toString(); // Modification time
917      size = stat.size.toString(); // File size
918    })
919
920    // Create an asset object.
921    let attachment: commonType.Asset = {
922      name: fileName,
923      uri: distributedUri,
924      path: filePath,
925      createTime: ctime,
926      modifyTime: mtime,
927      size: size,
928    }
929
930    // 3. Use the asset object as the root attribute of the distributed data object to create a distributed data object.
931    let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
932    let source: SourceObject = new SourceObject("jack", 18, false, parentSource, attachment);
933    this.d_object = distributedDataObject.create(this.context, source);
934
935    // Generate a network ID, activate the distributed data object, and save the data persistently.
936    // ...
937
938    return AbilityConstant.OnContinueResult.AGREE;
939  }
940}
941```
942
943The 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.
944
945> **NOTE**
946>
947> 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.
948
949```ts
950import { UIAbility, Want } from '@kit.AbilityKit';
951import { distributedDataObject, commonType } from '@kit.ArkData';
952import { hilog } from '@kit.PerformanceAnalysisKit';
953
954const TAG: string = '[MigrationAbility]';
955const DOMAIN_NUMBER: number = 0xFF00;
956
957export default class MigrationAbility extends UIAbility {
958  d_object?: distributedDataObject.DataObject;
959
960  handleDistributedData(want: Want) {
961    // ...
962    // Create an asset object whose attributes are empty.
963    let attachment: commonType.Asset = {
964      name: '',
965      uri: '',
966      path: '',
967      createTime: '',
968      modifyTime: '',
969      size: '',
970    }
971
972    // Use the empty asset object to create a distributed data object. Other basic attributes can be directly set to undefined.
973    let source: SourceObject = new SourceObject(undefined, undefined, undefined, undefined, attachment);
974    this.d_object = distributedDataObject.create(this.context, source);
975
976    this.d_object.on("status", (sessionId: string, networkId: string, status: 'online' | 'offline' | 'restored') => {
977      if (status == 'restored') {
978        if (this.d_object) {
979          // The restored event callback is received, indicating that the synchronization of the distributed asset object is complete.
980          hilog.info(DOMAIN_NUMBER, TAG, "restored attachment:" + JSON.stringify(this.d_object['attachment']));
981        }
982      }
983    });
984    // ...
985  }
986}
987```
988
989If you want to synchronize multiple assets, use either of the following methods:
990
991- 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.
992- 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.
993
994To 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:
995
996```ts
997// Import the modules.
998import { distributedDataObject, commonType } from '@kit.ArkData';
999import { UIAbility } from '@kit.AbilityKit';
1000
1001// Define a data object.
1002class SourceObject {
1003  name: string | undefined
1004  assets: Object | undefined // Add an object attribute to the distributed data object.
1005
1006  constructor(name: string | undefined, assets: Object | undefined) {
1007    this.name = name
1008    this.assets = assets;
1009  }
1010}
1011
1012export default class MigrationAbility extends UIAbility {
1013  d_object?: distributedDataObject.DataObject;
1014
1015  // This API is used to convert an asset array into a record.
1016  GetAssetsWapper(assets: commonType.Assets): Record<string, commonType.Asset> {
1017    let wrapper: Record<string, commonType.Asset> = {}
1018    let num: number = assets.length;
1019    for (let i: number = 0; i < num; i++) {
1020      wrapper[`asset${i}`] = assets[i];
1021    }
1022    return wrapper;
1023  }
1024
1025  async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {
1026    // ...
1027
1028    // Create multiple asset objects.
1029    let attachment1: commonType.Asset = {
1030      name: '',
1031      uri: '',
1032      path: '',
1033      createTime: '',
1034      modifyTime: '',
1035      size: '',
1036    }
1037
1038    let attachment2: commonType.Asset = {
1039      name: '',
1040      uri: '',
1041      path: '',
1042      createTime: '',
1043      modifyTime: '',
1044      size: '',
1045    }
1046
1047    // Insert the asset objects into the asset array.
1048    let assets: commonType.Assets = [];
1049    assets.push(attachment1);
1050    assets.push(attachment2);
1051
1052    // Convert the asset array into a record object and use it to create a distributed data object.
1053    let assetsWrapper: Object = this.GetAssetsWapper(assets);
1054    let source: SourceObject = new SourceObject("jack", assetsWrapper);
1055    this.d_object = distributedDataObject.create(this.context, source);
1056
1057    // ...
1058    return AbilityConstant.OnContinueResult.AGREE;
1059  }
1060}
1061```
1062
1063## Verification Guide
1064
1065A 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.
1066
1067> **NOTE**
1068>
1069> The screenshots in this section are for reference only. The DevEco Studio and SDK versions in use prevail.
1070
1071**Compiling and Installing the Demo**
1072
10731. [Switch to the full SDK](../faqs/full-sdk-switch-guide.md) to compile and install the mission center.
1074
10752. 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).
1076
10773. Build the project file.
1078
1079    1. Create an empty project and replace the corresponding folders with the downloaded files.
1080
1081        ![hop-cross-device-migration](figures/hop-cross-device-migration1.png)
1082
1083    2. Complete the signature, build, and installation.
1084        ​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.
1085           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**.
1086
1087           2. Choose **File > Project Structure**.
1088
1089           ![hop-cross-device-migration](figures/hop-cross-device-migration2.png)
1090
1091           3. Click **Signing Configs** and click **OK**.
1092
1093           ![hop-cross-device-migration](figures/hop-cross-device-migration3.png)
1094
1095           4. Connect to the developer board and run the demo.
1096
1097**Device Networking**
1098
10991. Open the calculators of devices A and B.
11002. Click the arrow in the upper right corner to select device B.
11013. Select a trusted device on device B. The PIN is displayed.
11024. Enter the PIN on device A.
11035. Verify the networking. Enter a number on device A. If the number is displayed on device B, the networking is successful.
1104
1105**Initiation Migration**
1106
11071. 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.
11082. Touch the name of device B. The application card list of device B is displayed.
11093. Drag the application card to be connected to the name of device A. The application on device A is started.
1110
1111## FAQs
1112
1113### Q1: What Can I Do If the Application Cannot Be Migrated Back to the Source Device?
1114
1115The 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#oncreate) 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#onnewwant).
1116
1117```ts
1118// MigrationAbility.ets
1119import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
1120import { hilog } from '@kit.PerformanceAnalysisKit';
1121
1122const TAG: string = '[MigrationAbility]';
1123const DOMAIN_NUMBER: number = 0xFF00;
1124
1125export default class MigrationAbility extends UIAbility {
1126  // ...
1127  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
1128    // ...
1129    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
1130      // ...
1131      // Set the migration state to ACTIVE when the launch is caused by migration. This setting copes with cold start.
1132      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
1133        hilog.info(DOMAIN_NUMBER, TAG, `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
1134      });
1135    }
1136    // ...
1137  }
1138
1139  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
1140    // ...
1141    // Set the migration state to ACTIVE when the launch is caused by migration. This setting copes with hot start.
1142    if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
1143      this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
1144        hilog.info(DOMAIN_NUMBER, TAG, `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
1145      });
1146    }
1147  }
1148  // ...
1149}
1150```
1151
1152### Q2: What Can I Do If the Call of loadContent() Does Not Take Effect in onWindowStageRestore()?
1153
1154If 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/arkts-apis-window-Window.md#loadcontent9) to trigger the loading of a specific page in the [onWindowStageRestore()](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onwindowstagerestore) lifecycle callback function, the loading does not take effect and the page in the page stack is still restored.
1155
1156