stage-ability-continuation.md 15.1 KB
Newer Older
1 2 3 4
# Ability Continuation Development

## When to Use

W
wusongqing 已提交
5
Ability 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 8

## Available APIs

W
wusongqing 已提交
9
The 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 12 13 14

**Table 1** Ability continuation APIs

|API| Description|
|:------ | :------|
W
wusongqing 已提交
15 16 17
| 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.|
| onCreate(want: Want, param: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page in the multi-instance ability scenario.|
| 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 22 23 24



**Figure 1** Ability continuation development

![continuation_dev](figures/continuation-info.png)

W
wusongqing 已提交
25
In 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.
W
wusongqing 已提交
26

27 28
## How to Develop

W
wusongqing 已提交
29 30
The code snippets provided below are all from [Sample](https://gitee.com/openharmony/ability_dmsfwk/tree/master/services/dtbschedmgr/test/samples/continuationManualTestSuite).

31 32
### Application Continuation

W
wusongqing 已提交
33
1. Modify the configuration file.
34 35 36 37

   - Configure the application to support ability continuation.

     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.
W
wusongqing 已提交
38 39
   
     ```javascript
W
wusongqing 已提交
40 41 42 43
     {
       "module": {
         "abilities": [
           {
44
             "continuable": true
W
wusongqing 已提交
45 46 47 48
           }
         ]
       }
     }
W
wusongqing 已提交
49 50
     ```
     
51
   
W
wusongqing 已提交
52
   - Configure the application startup type.
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
   
        If **launchType** is set to **multiton** 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).
        
        Configure a multi-instance application as follows:
        
        ```javascript
        {
          "module": {
            "abilities": [
              {
                "launchType": "multiton"
              }
            ]
          }
        }
        ```
        
        Configure a singleton application as follows or retain the default settings of **launchType**:
        
        ```javascript
        {
          "module": {
            "abilities": [
              {
                "launchType": "singleton"
              }
            ]
          }
        }
        ```
        
   - Apply for the distributed permissions.
W
wusongqing 已提交
85
     
86
        Declare the **DISTRIBUTED_DATASYNC** permission in the **module.json5** file for the application.
W
wusongqing 已提交
87
     
88 89 90 91 92 93
        ```javascript
        "requestPermissions": [
               {
                   "name": "ohos.permission.DISTRIBUTED_DATASYNC"
               },
        ```
W
wusongqing 已提交
94
     
95
        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:
W
wusongqing 已提交
96
     
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
        ```javascript
           requestPermissions = async () => {
               let permissions: Array<string> = [
                   "ohos.permission.DISTRIBUTED_DATASYNC"
               ];
               let needGrantPermission = false
               let accessManger = accessControl.createAtManager()
               Logger.info("app permission get bundle info")
               let bundleInfo = await bundle.getApplicationInfo(BUNDLE_NAME, 0, 100)
               Logger.info(`app permission query permission ${bundleInfo.accessTokenId.toString()}`)
               for (const permission of permissions) {
                   Logger.info(`app permission query grant status ${permission}`)
                   try {
                       let grantStatus = await accessManger.verifyAccessToken(bundleInfo.accessTokenId, permission)
                       if (grantStatus === PERMISSION_REJECT) {
                           needGrantPermission = true
                           break;
                       }
                   } catch (err) {
                       Logger.error(`app permission query grant status error ${permission} ${JSON.stringify(err)}`)
                       needGrantPermission = true
                       break;
                   }
               }
               if (needGrantPermission) {
                   Logger.info("app permission needGrantPermission")
                   try {
                       await accessManger.requestPermissionsFromUser(this.context, permissions)
                   } catch (err) {
                       Logger.error(`app permission ${JSON.stringify(err)}`)
                   }
               } else {
                   Logger.info("app permission already granted")
               }
W
wusongqing 已提交
131
           }
132 133
        ```
   
W
wusongqing 已提交
134 135 136 137


2. Implement the **onContinue()** API.

W
wusongqing 已提交
138
   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.
139 140 141 142 143 144 145 146

   Modules to import:

   ```javascript
   import Ability from '@ohos.application.Ability';
   import AbilityConstant from '@ohos.application.AbilityConstant';
   ```

W
wusongqing 已提交
147
   To implement ability continuation, you must implement this API and have the value **AGREE** returned.
W
wusongqing 已提交
148 149 150

   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.

151
   Example:
W
wusongqing 已提交
152

153
   ```javascript
W
wusongqing 已提交
154 155 156 157 158 159 160 161
        onContinue(wantParam : {[key: string]: any}) {
            Logger.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`)
            let workInput = AppStorage.Get<string>('ContinueWork');
            // Set the user input data into wantParam.
            wantParam["work"] = workInput // set user input data into want params
            Logger.info(`onContinue input = ${wantParam["input"]}`);
            return AbilityConstant.OnContinueResult.AGREE
        }
162
   ```
W
wusongqing 已提交
163 164

3. Implement the continuation logic in the **onCreate()** or **onNewWant()** API.
165

W
wusongqing 已提交
166
   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.
167

W
wusongqing 已提交
168 169 170 171
   The target device determines whether the startup is **LaunchReason.CONTINUATION** based on **launchReason** in **onCreate()**.
   
   After data restore is complete, call **restoreWindowStage** to trigger page restoration.
   
W
wusongqing 已提交
172
   You can also use **want.parameters.version** in the **want** parameter to obtain the application version number of the initiator.
173 174

   Example:
W
wusongqing 已提交
175
   
176
   ```javascript
W
wusongqing 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    import Ability from '@ohos.application.Ability';
    import distributedObject from '@ohos.data.distributedDataObject';
    
    export default class MainAbility extends Ability {
        storage : LocalStorag;

        onCreate(want, launchParam) {
            Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`)
            if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
                // Obtain the user data from the want parameter.
                let workInput = want.parameters.work
                Logger.info(`work input ${workInput}`)
                AppStorage.SetOrCreate<string>('ContinueWork', workInput)
   
                this.storage = new LocalStorage();
                this.context.restoreWindowStage(this.storage);
            }
        }
    }
196
   ```
W
wusongqing 已提交
197
For a singleton ability, use **onNewWant()** to achieve the same implementation.
198 199 200

### Data Continuation

W
wusongqing 已提交
201 202
Use distributed objects.

203
Distributed 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 [Sharing Distributed Data Objects](../database/data-sync-of-distributed-data-object.md).
W
wusongqing 已提交
204

205
In the ability continuation scenario, the distributed data object is used to synchronize the memory data from the local device to the target device. For details, see [Distributed Data Object Development](../database/database-distributedobject-guidelines.md).
W
wusongqing 已提交
206

W
wusongqing 已提交
207
- 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**.
W
wusongqing 已提交
208 209 210 211 212

  ```javascript
     import Ability from '@ohos.application.Ability';
     import distributedObject from '@ohos.data.distributedDataObject';
  
W
wusongqing 已提交
213
     var g_object = distributedObject.createDistributedObject({data:undefined});
W
wusongqing 已提交
214 215 216 217 218
  
     export default class MainAbility extends Ability {
         sessionId : string;
  
      onContinue(wantParam : {[key: string]: any}) {
W
wusongqing 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
        Logger.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`)

        if (g_object.__sessionId === undefined) {
            this.sessionId = distributedObject.genSessionId()
            Logger.info(`onContinue generate new sessionId`)
        }
        else {
            this.sessionId = g_object.__sessionId;
        }

        wantParam["session"] = this.sessionId
        g_object.data = AppStorage.Get<string>('ContinueStudy');
        Logger.info(`onContinue sessionId = ${this.sessionId}, name = ${g_object.data}`)
        g_object.setSessionId(this.sessionId);
        g_object.save(wantParam.targetDevice, (result, data)=>{
            Logger.info("save callback");
            Logger.info("save sessionId " + data.sessionId);
            Logger.info("save version " + data.version);
            Logger.info("save deviceId " + data.deviceId);
        });
W
wusongqing 已提交
239 240 241 242 243 244 245 246
  ```

- 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.

  ```javascript
     import Ability from '@ohos.application.Ability';
     import distributedObject from '@ohos.data.distributedDataObject';
  
W
wusongqing 已提交
247
     var g_object = distributedObject.createDistributedObject({data:undefined});
W
wusongqing 已提交
248 249
  
     export default class MainAbility extends Ability {
W
wusongqing 已提交
250
         storage : LocalStorag;
W
wusongqing 已提交
251 252 253 254 255
  
  
         onCreate(want, launchParam) {
             Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`)
             if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
256
              // Obtain the session ID of the distributed data object from the want parameter.
W
wusongqing 已提交
257 258 259
                 this.sessionId = want.parameters.session
                 Logger.info(`onCreate for continuation sessionId:  ${this.sessionId}`)
  
260
              // Before fetching data from the remote device, reset g_object.data to undefined.
W
wusongqing 已提交
261 262 263
                g_object.data = undefined;
                // Set the session ID, so the target will fetch data from the remote device.
                g_object.setSessionId(this.sessionId);
W
wusongqing 已提交
264
  
W
wusongqing 已提交
265 266 267
                AppStorage.SetOrCreate<string>('ContinueStudy', g_object.data)
                this.storage = new LocalStorage();
                this.context.restoreWindowStage(this.storage);
W
wusongqing 已提交
268
             }
W
wusongqing 已提交
269
             
W
wusongqing 已提交
270 271 272
         }
     }
  ```
W
wusongqing 已提交
273 274 275 276 277 278 279 280 281 282 283 284
  
### More Information

1. Timeout

   - 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.
   -  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.

2. 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.

### Restrictions

285
1.   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).
W
wusongqing 已提交
286 287 288 289
2.    Currently, the application can only implement the continuation capability. The continuation action must be initiated by the system.

### Best Practice

290
For 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.