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