• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Ability Call Development
2## When to Use
3Ability call is an extension of the ability capabilities. It enables an ability to be invoked by external systems. In this way, the ability can be displayed as a UI page on the foreground and created and run on the background. You can use the **Call** APIs to implement data sharing between different abilities through inter-process communication (IPC). There are two roles in the ability call: caller and callee. The following scenarios are involved in the ability call development:
4- Creating a callee
5- Accessing the callee
6
7The following figure shows the ability call process.
8
9![stage-call](figures/stage-call.png)
10
11> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br/>
12> The startup mode of the callee must be **singleton**.
13> Currently, only system applications and Service Extension abilities can use the **Call** APIs to access the callee.
14
15## Available APIs
16The table below describes the ability call APIs. For details, see [Ability](../reference/apis/js-apis-application-ability.md#caller).
17
18**Table 1** Ability call APIs
19|API|Description|
20|:------|:------|
21|startAbilityByCall(want: Want): Promise\<Caller>|Obtains the caller interface of the specified ability and, if the specified ability is not running, starts the ability in the background.|
22|on(method: string, callback: CaleeCallBack): void|Callback invoked when the callee registers a method.|
23|off(method: string): void|Callback invoked when the callee deregisters a method.|
24|call(method: string, data: rpc.Sequenceable): Promise\<void>|Sends agreed sequenceable data to the callee.|
25|callWithResult(method: string, data: rpc.Sequenceable): Promise\<rpc.MessageParcel>|Sends agreed sequenceable data to the callee and returns the agreed sequenceable data.|
26|release(): void|Releases the caller interface.|
27|onRelease(callback: OnReleaseCallBack): void|Registers a callback that is invoked when the caller is disconnected.|
28
29## How to Develop
30> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br/>
31> The sample code snippets provided in the **How to Develop** section are used to show specific development steps. They may not be able to run independently. For details about the complete project code, see [Samples](#samples).
32### Creating a Callee
33For the callee, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use the **on** API to register a listener. When data does not need to be received, use the **off** API to deregister the listener.
341. Configure the ability startup mode.
35
36  Set the ability of the callee to **singleton** in the **module.json5** file.
37
38|JSON Field|Description|
39|:------|:------|
40|"launchType"|Ability startup mode. Set this parameter to **singleton**.|
41
42An example of the ability configuration is as follows:
43```json
44"abilities":[{
45    "name": ".CalleeAbility",
46    "srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts",
47    "launchType": "singleton",
48    "description": "$string:CalleeAbility_desc",
49    "icon": "$media:icon",
50    "label": "$string:CalleeAbility_label",
51    "visible": true
52}]
53```
542. Import the **Ability** module.
55```
56import Ability from '@ohos.application.Ability'
57```
583. Define the agreed sequenceable data.
59
60  The data formats sent and received by the caller and callee must be consistent. In the following example, the data consists of numbers and strings. The sample code snippet is as follows:
61```ts
62export default class MySequenceable {
63    num: number = 0
64    str: String = ""
65
66    constructor(num, string) {
67        this.num = num
68        this.str = string
69    }
70
71    marshalling(messageParcel) {
72        messageParcel.writeInt(this.num)
73        messageParcel.writeString(this.str)
74        return true
75    }
76
77    unmarshalling(messageParcel) {
78        this.num = messageParcel.readInt()
79        this.str = messageParcel.readString()
80        return true
81    }
82}
83```
844. Implement **Callee.on** and **Callee.off**.
85
86  The time to register a listener for the callee 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 sample code snippet is as follows:
87```ts
88const TAG: string = '[CalleeAbility]'
89const MSG_SEND_METHOD: string = 'CallSendMsg'
90
91function sendMsgCallback(data) {
92    Logger.log(TAG, 'CalleeSortFunc called')
93
94    // Obtain the sequenceable data sent by the caller.
95    let receivedData = new MySequenceable(0, '')
96    data.readSequenceable(receivedData)
97    Logger.log(TAG, `receiveData[${receivedData.num}, ${receivedData.str}]`)
98
99    // Process the data.
100    // Return the sequenceable data result to the caller.
101    return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`)
102}
103
104export default class CalleeAbility extends Ability {
105    onCreate(want, launchParam) {
106        try {
107            this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
108        } catch (error) {
109            Logger.error(TAG, `${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
110        }
111    }
112
113    onDestroy() {
114        try {
115            this.callee.off(MSG_SEND_METHOD)
116        } catch (error) {
117            console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`)
118        }
119    }
120}
121```
122
123### Accessing the Callee
1241. Import the **Ability** module.
125```
126import Ability from '@ohos.application.Ability'
127```
1282. Obtain the caller interface.
129
130  The **context** attribute of the ability implements **startAbilityByCall** to obtain the caller interface of the ability. The following example uses **this.context** to obtain the **context** attribute of the **Ability** instance, uses **startAbilityByCall** to start the callee, obtain the caller interface, and register the **onRelease** listener of the caller. You need to implement processing based on service requirements. The sample code snippet is as follows:
131```ts
132async onButtonGetCaller() {
133    try {
134        this.caller = await context.startAbilityByCall({
135            bundleName: 'com.samples.CallApplication',
136            abilityName: 'CalleeAbility'
137        })
138        if (this.caller === undefined) {
139            Logger.error(TAG, 'get caller failed')
140            return
141        }
142        Logger.log(TAG, 'get caller success')
143        this.regOnRelease(this.caller)
144    } catch (error) {
145        Logger.error(TAG, `get caller failed with ${error}`)
146    }
147}.catch((error) => {
148    console.error(TAG + 'get caller failed with ' + error)
149})
150```
151  In the cross-device scenario, you need to specify the ID of the peer device. The sample code snippet is as follows:
152```ts
153let TAG = '[MainAbility] '
154var caller = undefined
155let context = this.context
156
157context.startAbilityByCall({
158    deviceId: getRemoteDeviceId(),
159    bundleName: 'com.samples.CallApplication',
160    abilityName: 'CalleeAbility'
161}).then((data) => {
162    if (data != null) {
163        caller = data
164        console.log(TAG + 'get remote caller success')
165        // Register the onRelease listener of the caller.
166        caller.onRelease((msg) => {
167            console.log(TAG + 'remote caller onRelease is called ' + msg)
168        })
169        console.log(TAG + 'remote caller register OnRelease succeed')
170    }
171}).catch((error) => {
172    console.error(TAG + 'get remote caller failed with ' + error)
173})
174```
175  Obtain the ID of the peer device from **DeviceManager**. Note that the **getTrustedDeviceListSync** API is open only to system applications. The sample code snippet is as follows:
176```ts
177import deviceManager from '@ohos.distributedHardware.deviceManager';
178var dmClass;
179function getRemoteDeviceId() {
180    if (typeof dmClass === 'object' && dmClass != null) {
181        var list = dmClass.getTrustedDeviceListSync();
182        if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
183            console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null");
184            return;
185        }
186        console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
187        return list[0].deviceId;
188    } else {
189        console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null");
190    }
191}
192```
193  In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code snippet is as follows:
194```ts
195let context = this.context
196let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
197context.requestPermissionsFromUser(permissions).then((data) => {
198    console.log("Succeed to request permission from user with data: "+ JSON.stringify(data))
199}).catch((error) => {
200    console.log("Failed to request permission from user with error: "+ JSON.stringify(error))
201})
202```
2033. Send agreed sequenceable data.
204
205  The sequenceable data can be sent to the callee with or without a return value. The method and sequenceable data must be consistent with those of the callee. The following example describes how to invoke the **Call** API to send data to the callee. The sample code snippet is as follows:
206```ts
207const MSG_SEND_METHOD: string = 'CallSendMsg'
208async onButtonCall() {
209    try {
210        let msg = new MySequenceable(1, 'origin_Msg')
211        await this.caller.call(MSG_SEND_METHOD, msg)
212    } catch (error) {
213        Logger.error(TAG, `caller call failed with ${error}`)
214    }
215}
216```
217
218  In the following, **CallWithResult** is used to send data **originMsg** to the callee and assign the data processed by the **CallSendMsg** method to **backMsg**. The sample code snippet is as follows:
219```ts
220const MSG_SEND_METHOD: string = 'CallSendMsg'
221originMsg: string = ''
222backMsg: string = ''
223async onButtonCallWithResult(originMsg, backMsg) {
224    try {
225        let msg = new MySequenceable(1, originMsg)
226        const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg)
227        Logger.log(TAG, 'caller callWithResult succeed')
228
229        let result = new MySequenceable(0, '')
230        data.readSequenceable(result)
231        backMsg(result.str)
232        Logger.log(TAG, `caller result is [${result.num}, ${result.str}]`)
233    } catch (error) {
234        Logger.error(TAG, `caller callWithResult failed with ${error}`)
235    }
236}
237```
2384. Release the caller interface.
239
240  When the caller interface is no longer required, call the **release** API to release it. The sample code snippet is as follows:
241```ts
242try {
243    this.caller.release()
244    this.caller = undefined
245    Logger.log(TAG, 'caller release succeed')
246} catch (error) {
247    Logger.error(TAG, `caller release failed with ${error}`)
248}
249```
250