stage-call.md 13.0 KB
Newer Older
W
wusongqing 已提交
1 2
# Ability Call Development
## When to Use
3
Ability call is an extension of the ability capability. It enables an ability to be invoked by and communicate with external systems. The ability invoked can be either started in the foreground or created and run in the background. You can use the ability call to implement data sharing between two abilities (caller ability and callee ability) through inter-process communication (IPC).
W
wusongqing 已提交
4

G
Gloria 已提交
5 6 7
The core API used for the ability call is **startAbilityByCall**, which differs from **startAbility** in the following ways:
 - **startAbilityByCall** supports ability startup in the foreground and background, whereas **startAbility** supports ability startup in the foreground only.
 - The caller ability can use the **Caller** object returned by **startAbilityByCall** to communicate with the callee ability, but **startAbility** does not provide the communication capability.
W
wusongqing 已提交
8

9 10 11 12 13 14 15 16 17
Ability call is usually used in the following scenarios:
- Communicating with the callee ability
- Starting the callee ability in the background

**Table 1** Terms used in the ability call
|Term|Description|
|:------|:------|
|Caller ability|Ability that triggers the ability call.|
|Callee ability|Ability invoked by the ability call.|
G
Gloria 已提交
18
|Caller       |Object returned by **startAbilityByCall** and used by the caller ability to communicate with the callee ability.|
19 20 21 22
|Callee       |Object held by the callee ability to communicate with the caller ability.|
|IPC          |Inter-process communication.|

The ability call process is as follows:
G
Gloria 已提交
23 24
 - The caller ability uses **startAbilityByCall** to obtain a **Caller** object and uses **call()** of the **Caller** object to send data to the callee ability.
 - The callee ability, which holds a **Callee** object, uses **on()** of the **Callee** object to register a callback. This callback is invoked when the callee ability receives data from the caller ability.
W
wusongqing 已提交
25 26
![stage-call](figures/stage-call.png)

27 28
> **NOTE**
>
G
Gloria 已提交
29
> The launch type of the callee ability must be **singleton**.
30
>
31
> Currently, only system applications can use the ability call.
W
wusongqing 已提交
32

W
wusongqing 已提交
33 34 35
## Available APIs
The table below describes the ability call APIs. For details, see [Ability](../reference/apis/js-apis-application-ability.md#caller).

36
**Table 2** Ability call APIs
W
wusongqing 已提交
37 38
|API|Description|
|:------|:------|
39
|startAbilityByCall(want: Want): Promise\<Caller>|Starts an ability in the foreground (through the **want** configuration) or background (default) and obtains the **Caller** object for communication with the ability. For details, see [AbilityContext](../reference/apis/js-apis-ability-context.md#abilitycontextstartabilitybycall) or **ServiceExtensionContext**.|
40 41 42 43
|on(method: string, callback: CalleeCallBack): void|Callback invoked when the callee ability registers a method.|
|off(method: string): void|Callback invoked when the callee ability deregisters a method.|
|call(method: string, data: rpc.Sequenceable): Promise\<void>|Sends agreed sequenceable data to the callee ability.|
|callWithResult(method: string, data: rpc.Sequenceable): Promise\<rpc.MessageParcel>|Sends agreed sequenceable data to the callee ability and obtains the agreed sequenceable data returned by the callee ability.|
G
Gloria 已提交
44 45
|release(): void|Releases the **Caller** object.|
|on(type: "release", callback: OnReleaseCallback): void|Callback invoked when the **Caller** object is released.|
W
wusongqing 已提交
46 47

## How to Develop
48 49 50
The procedure for developing the ability call is as follows:
1. Create a callee ability.
2. Access the callee ability.
G
Gloria 已提交
51

52
### Creating a Callee Ability
G
Gloria 已提交
53
For the callee ability, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener.
W
wusongqing 已提交
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 85 86 87 88 89 90 91 92 93 94 95 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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
1. **Configure the ability launch type.**

   Set **launchType** of the callee ability to **singleton** in the **module.json5** file.

   |JSON Field|Description|
   |:------|:------|
   |"launchType"|Ability launch type. Set this parameter to **singleton**.|

   An example of the ability configuration is as follows:
   
   ```json
   "abilities":[{
       "name": ".CalleeAbility",
       "srcEnty": "./ets/CalleeAbility/CalleeAbility.ts",
       "launchType": "singleton",
       "description": "$string:CalleeAbility_desc",
       "icon": "$media:icon",
       "label": "$string:CalleeAbility_label",
       "exported": true
   }]
   ```
   
2. **Import the ability module.**

   ```ts
   import Ability from '@ohos.app.ability.UIAbility';
   ```

3. **Define the agreed sequenceable data.**

   The data formats sent and received by the caller and callee abilities must be consistent. In the following example, the data formats are number and string. The code snippet is as follows:

   ```ts
   export default class MySequenceable {
       num: number = 0
       str: string = ""
   
       constructor(num, string) {
           this.num = num
           this.str = string
       }
   
       marshalling(messageParcel) {
           messageParcel.writeInt(this.num)
           messageParcel.writeString(this.str)
           return true
       }
   
       unmarshalling(messageParcel) {
           this.num = messageParcel.readInt()
           this.str = messageParcel.readString()
           return true
       }
   }
   ```

4. **Implement Callee.on and Callee.off.**

   The time to register a listener for the callee ability depends on your application. The data sent and received before the listener is registered and that after the listener is deregistered are not processed. In the following example, the **MSG_SEND_METHOD** listener is registered in **onCreate** of the ability and deregistered in **onDestroy**. After receiving sequenceable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. The code snippet is as follows:

   ```ts
   const TAG: string = '[CalleeAbility]'
   const MSG_SEND_METHOD: string = 'CallSendMsg'
   
   function sendMsgCallback(data) {
       console.log('CalleeSortFunc called')
   
       // Obtain the sequenceable data sent by the caller ability.
       let receivedData = new MySequenceable(0, '')
       data.readSequenceable(receivedData)
       console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`)
   
       // Process the data.
       // Return the sequenceable data result to the caller ability.
       return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`)
   }
   
   export default class CalleeAbility extends Ability {
       onCreate(want, launchParam) {
           try {
               this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
           } catch (error) {
               console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
           }
       }
   
       onDestroy() {
           try {
               this.callee.off(MSG_SEND_METHOD)
           } catch (error) {
               console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`)
           }
       }
   }
   ```
W
wusongqing 已提交
150

151
### Accessing the Callee Ability
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
1. **Import the Ability module.**

   ```ts
   import Ability from '@ohos.app.ability.UIAbility';
   ```

2. **Obtain the Caller object.**

   The **context** attribute of the ability implements **startAbilityByCall** to obtain the **Caller** object for communication. The following example uses **this.context** to obtain the **context** attribute of the ability, uses **startAbilityByCall** to start the callee ability, obtain the **Caller** object, and register the **onRelease** listener of the caller ability. You need to implement processing based on service requirements. The code snippet is as follows:

   ```ts
   // Register the onRelease listener of the caller ability.
   private regOnRelease(caller) {
       try {
           caller.on("release", (msg) => {
               console.log(`caller onRelease is called ${msg}`)
           })
           console.log('caller register OnRelease succeed')
       } catch (error) {
           console.log(`caller register OnRelease failed with ${error}`)
       }
   }
   
   async onButtonGetCaller() {
       try {
           this.caller = await context.startAbilityByCall({
               bundleName: 'com.samples.CallApplication',
               abilityName: 'CalleeAbility'
           })
           if (this.caller === undefined) {
               console.log('get caller failed')
               return
           }
           console.log('get caller success')
           this.regOnRelease(this.caller)
       } catch (error) {
           console.log(`get caller failed with ${error}`)
       }
   }
   ```

   In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows:

   ```ts
   async onButtonGetRemoteCaller() {
       var caller = undefined
       var context = this.context
   
       context.startAbilityByCall({
           deviceId: getRemoteDeviceId(),
           bundleName: 'com.samples.CallApplication',
           abilityName: 'CalleeAbility'
       }).then((data) => {
           if (data != null) {
               caller = data
               console.log('get remote caller success')
               // Register the onRelease listener of the caller ability.
               caller.on("release", (msg) => {
                   console.log(`remote caller onRelease is called ${msg}`)
               })
               console.log('remote caller register OnRelease succeed')
           }
       }).catch((error) => {
           console.error(`get remote caller failed with ${error}`)
       })
   }
   ```

   Obtain the ID of the peer device from **DeviceManager**. Note that the **getTrustedDeviceListSync** API is open only to system applications. The code snippet is as follows:

   ```ts
   import deviceManager from '@ohos.distributedHardware.deviceManager';
   var dmClass;
   function getRemoteDeviceId() {
       if (typeof dmClass === 'object' && dmClass != null) {
           var list = dmClass.getTrustedDeviceListSync()
           if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
               console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null")
               return
           }
           console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId)
           return list[0].deviceId
       } else {
           console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null")
       }
   }
   ```

   In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows:

   ```ts
   import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts';
   
   requestPermission() {
       let context = this.context
       let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
       let atManager = abilityAccessCtrl.createAtManager();
       atManager.requestPermissionsFromUser(context, permissions).then((data) => {
           console.log("Succeed to request permission from user with data: "+ JSON.stringify(data))
       }).catch((error) => {
           console.log("Failed to request permission from user with error: "+ JSON.stringify(error))
       })
   }
   ```

3. **Send agreed sequenceable data.**

   The sequenceable data can be sent to the callee ability with or without a return value. The method and sequenceable data must be consistent with those of the callee ability. The following example describes how to send data to the callee ability. The code snippet is as follows:

   ```ts
   const MSG_SEND_METHOD: string = 'CallSendMsg'
   async onButtonCall() {
       try {
           let msg = new MySequenceable(1, 'origin_Msg')
           await this.caller.call(MSG_SEND_METHOD, msg)
       } catch (error) {
           console.log(`caller call failed with ${error}`)
       }
   }
   ```

   In the following, **CallWithResult** is used to send data **originMsg** to the callee ability and assign the data processed by the **CallSendMsg** method to **backMsg**. The code snippet is as follows:

   ```ts
   const MSG_SEND_METHOD: string = 'CallSendMsg'
   originMsg: string = ''
   backMsg: string = ''
   async onButtonCallWithResult(originMsg, backMsg) {
       try {
           let msg = new MySequenceable(1, originMsg)
           const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg)
           console.log('caller callWithResult succeed')
   
           let result = new MySequenceable(0, '')
           data.readSequenceable(result)
           backMsg(result.str)
           console.log(`caller result is [${result.num}, ${result.str}]`)
       } catch (error) {
           console.log(`caller callWithResult failed with ${error}`)
       }
   }
   ```

4. **Release the Caller object.**

   When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows:

   ```ts
   releaseCall() {
       try {
           this.caller.release()
           this.caller = undefined
           console.log('caller release succeed')
       } catch (error) {
           console.log(`caller release failed with ${error}`)
       }
   }
   ```