• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Ability Call Development
2## When to Use
3Ability 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).
4
5The core API used for the ability call is **startAbilityByCall**, which differs from **startAbility** in the following ways:
6 - **startAbilityByCall** supports ability startup in the foreground and background, whereas **startAbility** supports ability startup in the foreground only.
7 - 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.
8
9Ability call is usually used in the following scenarios:
10- Communicating with the callee ability
11- Starting the callee ability in the background
12
13**Table 1** Terms used in the ability call
14|Term|Description|
15|:------|:------|
16|Caller ability|Ability that triggers the ability call.|
17|Callee ability|Ability invoked by the ability call.|
18|Caller       |Object returned by **startAbilityByCall** and used by the caller ability to communicate with the callee ability.|
19|Callee       |Object held by the callee ability to communicate with the caller ability.|
20|IPC          |Inter-process communication.|
21
22The ability call process is as follows:
23 - The caller ability uses **startAbilityByCall** to obtain a **Caller** object and uses **call()** of the **Caller** object to send data to the callee ability.
24 - 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.
25![stage-call](figures/stage-call.png)
26
27> **NOTE**
28>
29> The launch type of the callee ability must be **singleton**.
30>
31> Currently, only system applications can use the ability call.
32
33## Available APIs
34The table below describes the ability call APIs. For details, see [Ability](../reference/apis/js-apis-application-ability.md#caller).
35
36**Table 2** Ability call APIs
37|API|Description|
38|:------|:------|
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|on(method: string, callback: CalleeCallBack): void|Callback invoked when the callee ability registers a method.|
41|off(method: string): void|Callback invoked when the callee ability deregisters a method.|
42|call(method: string, data: rpc.Sequenceable): Promise\<void>|Sends agreed sequenceable data to the callee ability.|
43|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.|
44|release(): void|Releases the **Caller** object.|
45|on(type: "release", callback: OnReleaseCallback): void|Callback invoked when the **Caller** object is released.|
46
47## How to Develop
48The procedure for developing the ability call is as follows:
491. Create a callee ability.
502. Access the callee ability.
51
52### Creating a Callee Ability
53For 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.
54
551. **Configure the ability launch type.**
56
57   Set **launchType** of the callee ability to **singleton** in the **module.json5** file.
58
59   |JSON Field|Description|
60   |:------|:------|
61   |"launchType"|Ability launch type. Set this parameter to **singleton**.|
62
63   An example of the ability configuration is as follows:
64
65   ```json
66   "abilities":[{
67       "name": ".CalleeAbility",
68       "srcEnty": "./ets/CalleeAbility/CalleeAbility.ts",
69       "launchType": "singleton",
70       "description": "$string:CalleeAbility_desc",
71       "icon": "$media:icon",
72       "label": "$string:CalleeAbility_label",
73       "exported": true
74   }]
75   ```
76
772. **Import the ability module.**
78
79   ```ts
80   import Ability from '@ohos.app.ability.UIAbility';
81   ```
82
833. **Define the agreed sequenceable data.**
84
85   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:
86
87   ```ts
88   export default class MySequenceable {
89       num: number = 0
90       str: string = ""
91
92       constructor(num, string) {
93           this.num = num
94           this.str = string
95       }
96
97       marshalling(messageParcel) {
98           messageParcel.writeInt(this.num)
99           messageParcel.writeString(this.str)
100           return true
101       }
102
103       unmarshalling(messageParcel) {
104           this.num = messageParcel.readInt()
105           this.str = messageParcel.readString()
106           return true
107       }
108   }
109   ```
110
1114. **Implement Callee.on and Callee.off.**
112
113   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:
114
115   ```ts
116   const TAG: string = '[CalleeAbility]'
117   const MSG_SEND_METHOD: string = 'CallSendMsg'
118
119   function sendMsgCallback(data) {
120       console.log('CalleeSortFunc called')
121
122       // Obtain the sequenceable data sent by the caller ability.
123       let receivedData = new MySequenceable(0, '')
124       data.readSequenceable(receivedData)
125       console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`)
126
127       // Process the data.
128       // Return the sequenceable data result to the caller ability.
129       return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`)
130   }
131
132   export default class CalleeAbility extends Ability {
133       onCreate(want, launchParam) {
134           try {
135               this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
136           } catch (error) {
137               console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
138           }
139       }
140
141       onDestroy() {
142           try {
143               this.callee.off(MSG_SEND_METHOD)
144           } catch (error) {
145               console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`)
146           }
147       }
148   }
149   ```
150
151### Accessing the Callee Ability
1521. **Import the Ability module.**
153
154   ```ts
155   import Ability from '@ohos.app.ability.UIAbility';
156   ```
157
1582. **Obtain the Caller object.**
159
160   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:
161
162   ```ts
163   // Register the onRelease listener of the caller ability.
164   private regOnRelease(caller) {
165       try {
166           caller.on("release", (msg) => {
167               console.log(`caller onRelease is called ${msg}`)
168           })
169           console.log('caller register OnRelease succeed')
170       } catch (error) {
171           console.log(`caller register OnRelease failed with ${error}`)
172       }
173   }
174
175   async onButtonGetCaller() {
176       try {
177           this.caller = await context.startAbilityByCall({
178               bundleName: 'com.samples.CallApplication',
179               abilityName: 'CalleeAbility'
180           })
181           if (this.caller === undefined) {
182               console.log('get caller failed')
183               return
184           }
185           console.log('get caller success')
186           this.regOnRelease(this.caller)
187       } catch (error) {
188           console.log(`get caller failed with ${error}`)
189       }
190   }
191   ```
192
193   In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows:
194
195   ```ts
196   async onButtonGetRemoteCaller() {
197       var caller = undefined
198       var context = this.context
199
200       context.startAbilityByCall({
201           deviceId: getRemoteDeviceId(),
202           bundleName: 'com.samples.CallApplication',
203           abilityName: 'CalleeAbility'
204       }).then((data) => {
205           if (data != null) {
206               caller = data
207               console.log('get remote caller success')
208               // Register the onRelease listener of the caller ability.
209               caller.on("release", (msg) => {
210                   console.log(`remote caller onRelease is called ${msg}`)
211               })
212               console.log('remote caller register OnRelease succeed')
213           }
214       }).catch((error) => {
215           console.error(`get remote caller failed with ${error}`)
216       })
217   }
218   ```
219
220   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:
221
222   ```ts
223   import deviceManager from '@ohos.distributedHardware.deviceManager';
224   var dmClass;
225   function getRemoteDeviceId() {
226       if (typeof dmClass === 'object' && dmClass != null) {
227           var list = dmClass.getTrustedDeviceListSync()
228           if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
229               console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null")
230               return
231           }
232           console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId)
233           return list[0].deviceId
234       } else {
235           console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null")
236       }
237   }
238   ```
239
240   In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows:
241
242   ```ts
243   import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts';
244
245   requestPermission() {
246       let context = this.context
247       let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
248       let atManager = abilityAccessCtrl.createAtManager();
249       atManager.requestPermissionsFromUser(context, permissions).then((data) => {
250           console.log("Succeed to request permission from user with data: "+ JSON.stringify(data))
251       }).catch((error) => {
252           console.log("Failed to request permission from user with error: "+ JSON.stringify(error))
253       })
254   }
255   ```
256
2573. **Send agreed sequenceable data.**
258
259   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:
260
261   ```ts
262   const MSG_SEND_METHOD: string = 'CallSendMsg'
263   async onButtonCall() {
264       try {
265           let msg = new MySequenceable(1, 'origin_Msg')
266           await this.caller.call(MSG_SEND_METHOD, msg)
267       } catch (error) {
268           console.log(`caller call failed with ${error}`)
269       }
270   }
271   ```
272
273   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:
274
275   ```ts
276   const MSG_SEND_METHOD: string = 'CallSendMsg'
277   originMsg: string = ''
278   backMsg: string = ''
279   async onButtonCallWithResult(originMsg, backMsg) {
280       try {
281           let msg = new MySequenceable(1, originMsg)
282           const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg)
283           console.log('caller callWithResult succeed')
284
285           let result = new MySequenceable(0, '')
286           data.readSequenceable(result)
287           backMsg(result.str)
288           console.log(`caller result is [${result.num}, ${result.str}]`)
289       } catch (error) {
290           console.log(`caller callWithResult failed with ${error}`)
291       }
292   }
293   ```
294
2954. **Release the Caller object.**
296
297   When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows:
298
299   ```ts
300   releaseCall() {
301       try {
302           this.caller.release()
303           this.caller = undefined
304           console.log('caller release succeed')
305       } catch (error) {
306           console.log(`caller release failed with ${error}`)
307       }
308   }
309   ```