1# Ability Continuation Development 2 3## When to Use 4 5Ability continuation is to continue the current mission of an application, including the UI component state variables and distributed objects, on another device. The UI component state variables are used to synchronize UI data, and the distributed objects are used to synchronize memory data. 6 7## Available APIs 8 9The following table lists the APIs used for ability continuation. For details about the APIs, see [Ability](../reference/apis/js-apis-application-ability.md). 10 11**Table 1** Ability continuation APIs 12 13|API| Description| 14|:------ | :------| 15| onContinue(wantParam : {[key: string]: any}): OnContinueResult | Called by the initiator to store the data required for continuation. The return value indicates whether the continuation request is accepted. The value **AGREE** means that the continuation request is accepted, **REJECT** means that the continuation request is rejected, and **MISMATCH** means a version mismatch.| 16| onCreate(want: Want, param: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page in the multi-instance ability scenario.| 17| onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page in the singleton ability scenario.| 18 19 20 21**Figure 1** Ability continuation development 22 23 24 25In effect, ability continuation is a cross-device ability startup that carries data. When a continuation action is initiated, the system on device A calls back **onContinue()** of the application. You must implement storage of the current data in this API. Then, the system initiates a cross-device ability startup on device B and transmits the data to device B. The system on device B calls back **onCreate()** or **onNewWant()**. You must implement restoration of the transmitted data in this API. 26 27## How to Develop 28 29The code snippets provided below are all from [Sample](https://gitee.com/openharmony/ability_dmsfwk/tree/master/services/dtbschedmgr/test/samples/continuationManualTestSuite). 30 31### Application Continuation 32 331. Modify the configuration file. 34 35 - Configure the application to support ability continuation. 36 37 Set the **continuable** field in the **module.json5** file to **true**. The default value is **false**. If this parameter is set to **false**, the application cannot be continued on another device. 38 39 ```javascript 40 { 41 "module": { 42 "abilities": [ 43 { 44 "continuable": true 45 } 46 ] 47 } 48 } 49 ``` 50 51 52 53 54 - Configure the application startup type. 55 56 If **launchType** is set to **standard** in the **module.json5** file, the application is of the multi-instance launch type. During ability continuation, regardless of whether the application is already open, the target starts the application and restores the UI page. If **launchType** is set to **singleton**, the application is of the singleton launch type. If the application is already open, the target clears the existing page stack and restores the UI page. For more information, see "Launch Type" in [Ability Development](./stage-ability.md). 57 58 Configure a multi-instance application as follows: 59 60 ```javascript 61 { 62 "module": { 63 "abilities": [ 64 { 65 "launchType": "standard" 66 } 67 ] 68 } 69 } 70 ``` 71 72 Configure a singleton application as follows or retain the default settings of **launchType**: 73 74 ```javascript 75 { 76 "module": { 77 "abilities": [ 78 { 79 "launchType": "singleton" 80 } 81 ] 82 } 83 } 84 ``` 85 86 87 88 - Apply for the distributed permissions. 89 90 Declare the **DISTRIBUTED_DATASYNC** permission in the **module.json5** file for the application. 91 92 ```javascript 93 "requestPermissions": [ 94 { 95 "name": "ohos.permission.DISTRIBUTED_DATASYNC" 96 }, 97 ``` 98 99 100 101 This permission must be granted by the user in a dialog box when the application is started for the first time. To enable the application to display a dialog box to ask for the permission, add the following code to **onWindowStageCreate** of the **Ability** class: 102 103 ```javascript 104 requestPermissions = async () => { 105 let permissions: Array<string> = [ 106 "ohos.permission.DISTRIBUTED_DATASYNC" 107 ]; 108 let needGrantPermission = false 109 let accessManger = accessControl.createAtManager() 110 Logger.info("app permission get bundle info") 111 let bundleInfo = await bundle.getApplicationInfo(BUNDLE_NAME, 0, 100) 112 Logger.info(`app permission query permission ${bundleInfo.accessTokenId.toString()}`) 113 for (const permission of permissions) { 114 Logger.info(`app permission query grant status ${permission}`) 115 try { 116 let grantStatus = await accessManger.verifyAccessToken(bundleInfo.accessTokenId, permission) 117 if (grantStatus === PERMISSION_REJECT) { 118 needGrantPermission = true 119 break; 120 } 121 } catch (err) { 122 Logger.error(`app permission query grant status error ${permission} ${JSON.stringify(err)}`) 123 needGrantPermission = true 124 break; 125 } 126 } 127 if (needGrantPermission) { 128 Logger.info("app permission needGrantPermission") 129 try { 130 await accessManger.requestPermissionsFromUser(this.context, permissions) 131 } catch (err) { 132 Logger.error(`app permission ${JSON.stringify(err)}`) 133 } 134 } else { 135 Logger.info("app permission already granted") 136 } 137 } 138 ``` 139 140 141 142 1432. Implement the **onContinue()** API. 144 145 The **onContinue()** API is called by the initiator to save the UI component state variables and memory data and prepare for continuation. After the application completes the continuation preparation, the system must return either **OnContinueResult.AGREE(0)** to accept the continuation request or an error code to reject the request. If this API is not implemented, the system rejects the continuation request by default. 146 147 Modules to import: 148 149 ```javascript 150 import Ability from '@ohos.application.Ability'; 151 import AbilityConstant from '@ohos.application.AbilityConstant'; 152 ``` 153 154 To implement ability continuation, you must implement this API and have the value **AGREE** returned. 155 156 You can obtain the target device ID (identified by the key **targetDevice**) and the version number (identified by the key **version**) of the application installed on the target device from the **wantParam** parameter of this API. The version number can be used for compatibility check. If the current application version is incompatible with that on the target device, **OnContinueResult.MISMATCH** can be returned to reject the continuation request. 157 158 Example 159 160 ```javascript 161 onContinue(wantParam : {[key: string]: any}) { 162 Logger.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`) 163 let workInput = AppStorage.Get<string>('ContinueWork'); 164 // Set the user input data into wantParam. 165 wantParam["work"] = workInput // set user input data into want params 166 Logger.info(`onContinue input = ${wantParam["input"]}`); 167 return AbilityConstant.OnContinueResult.AGREE 168 } 169 ``` 170 171 172 1733. Implement the continuation logic in the **onCreate()** or **onNewWant()** API. 174 175 The **onCreate()** API is called by the target. When the ability is started on the target device, this API is called to instruct the application to synchronize the memory data and UI component state, and triggers page restoration after the synchronization is complete. If the continuation logic is not implemented, the ability will be started in common startup mode and the page cannot be restored. 176 177 The target device determines whether the startup is **LaunchReason.CONTINUATION** based on **launchReason** in **onCreate()**. 178 179 After data restore is complete, call **restoreWindowStage** to trigger page restoration. 180 181 182 183 You can also use **want.parameters.version** in the **want** parameter to obtain the application version number of the initiator. 184 185 Example 186 187 ```javascript 188 import Ability from '@ohos.application.Ability'; 189 import distributedObject from '@ohos.data.distributedDataObject'; 190 191 export default class MainAbility extends Ability { 192 storage : LocalStorag; 193 194 onCreate(want, launchParam) { 195 Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`) 196 if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { 197 // Obtain the user data from the want parameter. 198 let workInput = want.parameters.work 199 Logger.info(`work input ${workInput}`) 200 AppStorage.SetOrCreate<string>('ContinueWork', workInput) 201 202 this.storage = new LocalStorage(); 203 this.context.restoreWindowStage(this.storage); 204 } 205 } 206 } 207 ``` 208For a singleton ability, use **onNewWant()** to achieve the same implementation. 209 210### Data Continuation 211 212Use distributed objects. 213 214Distributed objects allow cross-device data synchronization like local variables. For two devices that form a Super Device, when data in the distributed data object of an application is added, deleted, or modified on a device, the data for the same application is also updated on the other device. Both devices can listen for the data changes and online and offline states of the other. For details, see [Distributed Data Object Development](../database/database-distributedobject-guidelines.md). 215 216In the ability continuation scenario, the distributed data object is used to synchronize the memory data from the local device to the target device. 217 218- In **onContinue()**, the initiator saves the data to be migrated to the distributed object, calls the **save()** API to save the data and synchronize the data to the target device, sets the session ID, and sends the session ID to the target device through **wantParam**. 219 220 ```javascript 221 import Ability from '@ohos.application.Ability'; 222 import distributedObject from '@ohos.data.distributedDataObject'; 223 224 var g_object = distributedObject.createDistributedObject({data:undefined}); 225 226 export default class MainAbility extends Ability { 227 sessionId : string; 228 229 onContinue(wantParam : {[key: string]: any}) { 230 Logger.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`) 231 232 if (g_object.__sessionId === undefined) { 233 this.sessionId = distributedObject.genSessionId() 234 Logger.info(`onContinue generate new sessionId`) 235 } 236 else { 237 this.sessionId = g_object.__sessionId; 238 } 239 240 wantParam["session"] = this.sessionId 241 g_object.data = AppStorage.Get<string>('ContinueStudy'); 242 Logger.info(`onContinue sessionId = ${this.sessionId}, name = ${g_object.data}`) 243 g_object.setSessionId(this.sessionId); 244 g_object.save(wantParam.targetDevice, (result, data)=>{ 245 Logger.info("save callback"); 246 Logger.info("save sessionId " + data.sessionId); 247 Logger.info("save version " + data.version); 248 Logger.info("save deviceId " + data.deviceId); 249 }); 250 ``` 251 252 253 254- The target device obtains the session ID from **onCreate()**, creates a distributed object, and associates the distributed object with the session ID. In this way, the distributed object can be synchronized. Before calling **restoreWindowStage**, ensure that all distributed objects required for continuation have been associated. 255 256 ```javascript 257 import Ability from '@ohos.application.Ability'; 258 import distributedObject from '@ohos.data.distributedDataObject'; 259 260 var g_object = distributedObject.createDistributedObject({data:undefined}); 261 262 export default class MainAbility extends Ability { 263 storage : LocalStorag; 264 265 266 onCreate(want, launchParam) { 267 Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`) 268 if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { 269 // Obtain the session ID of the distributed data object from the want parameter. 270 this.sessionId = want.parameters.session 271 Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`) 272 273 // Before fetching data from the remote device, reset g_object.data to undefined. 274 g_object.data = undefined; 275 // Set the session ID, so the target will fetch data from the remote device. 276 g_object.setSessionId(this.sessionId); 277 278 AppStorage.SetOrCreate<string>('ContinueStudy', g_object.data) 279 this.storage = new LocalStorage(); 280 this.context.restoreWindowStage(this.storage); 281 } 282 283 } 284 } 285 ``` 286 287 288 289### More Information 290 2911. Timeout 292 293 - If the application to be continued is not installed on the target device, the system checks whether the application can be installed on it and waits for a response for 4 seconds. If no response is received within 4 seconds, the caller receives a timeout error code, which means that the application cannot be installed on the target device. If the application can be installed, the system prompts the consumer to install the application on the target device. The consumer can initiate the continuation again after the installation. 294 - If the application to be continued has been installed on the target device, the system waits for a response to the continuation request for 20 seconds. If no response is received within 20 seconds, the caller receives a timeout error code, which means that the continuation fails. 295 2962. By default, the system supports page stack information migration, which means that the page stack of the initiator will be automatically migrated to the target device. No adaptation is required. 297 298 299 300### Restrictions 301 3021. The continuation must be performed between the same ability, which means the same bundle name, module name, and ability name. For details, see [Application Package Structure Configuration File](../quick-start/module-configuration-file.md). 3032. Currently, the application can only implement the continuation capability. The continuation action must be initiated by the system. 304 305 306 307### Best Practice 308 309For better user experience, you are advised to use the **wantParam** parameter to transmit data smaller than 100 KB and use distributed objects to transmit data larger than 100 KB. 310