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