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