• 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.
50
512. Access the callee ability.
52
53### Creating a Callee Ability
54For 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.
55**1. 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
63An example of the ability configuration is as follows:
64```json
65"abilities":[{
66    "name": ".CalleeAbility",
67    "srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts",
68    "launchType": "singleton",
69    "description": "$string:CalleeAbility_desc",
70    "icon": "$media:icon",
71    "label": "$string:CalleeAbility_label",
72    "visible": true
73}]
74```
75**2. Import the Ability module.**
76```ts
77import Ability from '@ohos.app.ability.UIAbility'
78```
79**3. Define the agreed sequenceable data.**
80
81  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:
82```ts
83export default class MySequenceable {
84    num: number = 0
85    str: string = ""
86
87    constructor(num, string) {
88        this.num = num
89        this.str = string
90    }
91
92    marshalling(messageParcel) {
93        messageParcel.writeInt(this.num)
94        messageParcel.writeString(this.str)
95        return true
96    }
97
98    unmarshalling(messageParcel) {
99        this.num = messageParcel.readInt()
100        this.str = messageParcel.readString()
101        return true
102    }
103}
104```
105**4. Implement Callee.on and Callee.off.**
106
107  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:
108```ts
109const TAG: string = '[CalleeAbility]'
110const MSG_SEND_METHOD: string = 'CallSendMsg'
111
112function sendMsgCallback(data) {
113    console.log('CalleeSortFunc called')
114
115    // Obtain the sequenceable data sent by the caller ability.
116    let receivedData = new MySequenceable(0, '')
117    data.readSequenceable(receivedData)
118    console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`)
119
120    // Process the data.
121    // Return the sequenceable data result to the caller ability.
122    return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`)
123}
124
125export default class CalleeAbility extends Ability {
126    onCreate(want, launchParam) {
127        try {
128            this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
129        } catch (error) {
130            console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
131        }
132    }
133
134    onDestroy() {
135        try {
136            this.callee.off(MSG_SEND_METHOD)
137        } catch (error) {
138            console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`)
139        }
140    }
141}
142```
143
144### Accessing the Callee Ability
145**1. Import the Ability module.**
146```ts
147import Ability from '@ohos.app.ability.UIAbility'
148```
149**2. Obtain the Caller object.**
150
151  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:
152```ts
153// Register the onRelease listener of the caller ability.
154private regOnRelease(caller) {
155    try {
156        caller.on("release", (msg) => {
157            console.log(`caller onRelease is called ${msg}`)
158        })
159        console.log('caller register OnRelease succeed')
160    } catch (error) {
161        console.log(`caller register OnRelease failed with ${error}`)
162    }
163}
164
165async onButtonGetCaller() {
166    try {
167        this.caller = await context.startAbilityByCall({
168            bundleName: 'com.samples.CallApplication',
169            abilityName: 'CalleeAbility'
170        })
171        if (this.caller === undefined) {
172            console.log('get caller failed')
173            return
174        }
175        console.log('get caller success')
176        this.regOnRelease(this.caller)
177    } catch (error) {
178        console.log(`get caller failed with ${error}`)
179    }
180}
181```
182  In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows:
183```ts
184async onButtonGetRemoteCaller() {
185    var caller = undefined
186    var context = this.context
187
188    context.startAbilityByCall({
189        deviceId: getRemoteDeviceId(),
190        bundleName: 'com.samples.CallApplication',
191        abilityName: 'CalleeAbility'
192    }).then((data) => {
193        if (data != null) {
194            caller = data
195            console.log('get remote caller success')
196            // Register the onRelease listener of the caller ability.
197            caller.on("release", (msg) => {
198                console.log(`remote caller onRelease is called ${msg}`)
199            })
200            console.log('remote caller register OnRelease succeed')
201        }
202    }).catch((error) => {
203        console.error(`get remote caller failed with ${error}`)
204    })
205}
206```
207  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:
208```ts
209import deviceManager from '@ohos.distributedHardware.deviceManager';
210var dmClass;
211function getRemoteDeviceId() {
212    if (typeof dmClass === 'object' && dmClass != null) {
213        var list = dmClass.getTrustedDeviceListSync()
214        if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
215            console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null")
216            return
217        }
218        console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId)
219        return list[0].deviceId
220    } else {
221        console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null")
222    }
223}
224```
225  In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows:
226```ts
227import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts';
228
229requestPermission() {
230    let context = this.context
231    let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
232    let atManager = abilityAccessCtrl.createAtManager();
233    atManager.requestPermissionsFromUser(context, permissions).then((data) => {
234        console.log("Succeed to request permission from user with data: "+ JSON.stringify(data))
235    }).catch((error) => {
236        console.log("Failed to request permission from user with error: "+ JSON.stringify(error))
237    })
238}
239```
240**3. Send agreed sequenceable data.**
241
242  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:
243```ts
244const MSG_SEND_METHOD: string = 'CallSendMsg'
245async onButtonCall() {
246    try {
247        let msg = new MySequenceable(1, 'origin_Msg')
248        await this.caller.call(MSG_SEND_METHOD, msg)
249    } catch (error) {
250        console.log(`caller call failed with ${error}`)
251    }
252}
253```
254
255  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:
256```ts
257const MSG_SEND_METHOD: string = 'CallSendMsg'
258originMsg: string = ''
259backMsg: string = ''
260async onButtonCallWithResult(originMsg, backMsg) {
261    try {
262        let msg = new MySequenceable(1, originMsg)
263        const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg)
264        console.log('caller callWithResult succeed')
265
266        let result = new MySequenceable(0, '')
267        data.readSequenceable(result)
268        backMsg(result.str)
269        console.log(`caller result is [${result.num}, ${result.str}]`)
270    } catch (error) {
271        console.log(`caller callWithResult failed with ${error}`)
272    }
273}
274```
275**4. Release the Caller object.**
276
277  When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows:
278```ts
279releaseCall() {
280    try {
281        this.caller.release()
282        this.caller = undefined
283        console.log('caller release succeed')
284    } catch (error) {
285        console.log(`caller release failed with ${error}`)
286    }
287}
288```
289