• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Call调用开发指导
2## 场景介绍
3Call调用是Ability能力的扩展,它为Ability提供一种能够被外部调用并与外部进行通信的能力。Call调用支持前台与后台两种启动方式,使Ability既能被拉起到前台展示UI,也可以在后台被创建并运行。Call调用在调用方与被调用方间建立了IPC通信,因此应用开发者可通过Call调用实现不同Ability之间的数据共享。
4
5Call调用的核心接口是startAbilityByCall方法,与startAbility接口的不同之处在于:
6 - startAbilityByCall支持前台与后台两种启动方式,而startAbility仅支持前台启动。
7 - 调用方可使用startAbilityByCall所返回的Caller对象与被调用方进行通信,而startAbilty不具备通信能力。
8
9Call调用的使用场景主要包括:
10- 需要与被启动的Ability进行通信
11- 希望被启动的Ability在后台运行
12
13**表1** Call调用相关名词解释
14|名词|描述|
15|:------|:------|
16|CallerAbility|指代进行Call调用的Ability(调用方)|
17|CalleeAbility|指代被Call调用的Ability(被调用方)|
18|Caller       |实际对象,由startAbilityByCall接口所返回,CallerAbility可使用Caller与CalleeAbility进行通信,具体接口见表2|
19|Callee       |实际对象,被CalleeAbility持有,可与Caller进行通信|
20|IPC          |指代进程间通信|
21
22Call调用流程示意图如下:
23 - CallerAbility调用startAbilityByCall接口获取Caller,并使用Caller对象的call方法向CalleeAbility发送数据
24 - CalleeAbility持有一个Callee对象,通过Callee的on方法注册回调函数,当接收到Caller发送的数据时将会调用对应的回调函数
25![stage-call](figures/stage-call.png)
26
27> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
28> CalleeAbility的启动模式需要为单实例。
29> 当前仅支持系统应用使用Call调用。
30
31## 接口说明
32Caller及Callee功能如下:具体的API详见[接口文档](../reference/apis/js-apis-application-ability.md#caller)。
33
34**表2** Call API接口功能介绍
35|接口名|描述|
36|:------|:------|
37|startAbilityByCall(want: Want): Promise\<Caller>|启动指定Ability并获取其Caller通信接口,默认为后台启动,通过配置want可实现前台启动,详见[接口文档](../reference/apis/js-apis-ability-context.md#abilitycontextstartabilitybycall)。AbilityContext与ServiceExtensionContext均支持该接口。|
38|on(method: string, callback: CalleeCallBack): void|通用组件Callee注册method对应的callback方法。|
39|off(method: string): void|通用组件Callee解注册method的callback方法。|
40|call(method: string, data: rpc.Sequenceable): Promise\<void>|向通用组件Callee发送约定序列化数据。|
41|callWithResult(method: string, data: rpc.Sequenceable): Promise\<rpc.MessageParcel>|向通用组件Callee发送约定序列化数据, 并将Callee返回的约定序列化数据带回。|
42|release(): void|释放通用组件的Caller通信接口。|
43|on(type: "release", callback: OnReleaseCallback): void|注册通用组件通信断开监听通知。|
44
45## 开发步骤
46Call调用的开发步骤:
47- 创建Callee被调用端。
48- 访问Callee被调用端。
49
50### 创建Callee被调用端
51Callee被调用端,需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过on接口注册监听,无需接收数据时通过off接口解除监听。
52**1. 配置Ability的启动模式**
53
54  配置module.json5,将CalleeAbility配置为单实例"singleton"。
55
56|Json字段|字段说明|
57|:------|:------|
58|"launchType"|Ability的启动模式,设置为"singleton"类型。 |
59
60Ability配置标签示例如下:
61```json
62"abilities":[{
63    "name": ".CalleeAbility",
64    "srcEnty": "./ets/CalleeAbility/CalleeAbility.ts",
65    "launchType": "singleton",
66    "description": "$string:CalleeAbility_desc",
67    "icon": "$media:icon",
68    "label": "$string:CalleeAbility_label",
69    "exported": true
70}]
71```
72**2. 导入Ability模块**
73```ts
74import Ability from '@ohos.app.ability.UIAbility'
75```
76**3. 定义约定的序列化数据**
77
78  调用端及被调用端发送接收的数据格式需协商一致,如下示例约定数据由number和string组成。具体示例代码如下:
79```ts
80export default class MySequenceable {
81    num: number = 0
82    str: string = ""
83
84    constructor(num, string) {
85        this.num = num
86        this.str = string
87    }
88
89    marshalling(messageParcel) {
90        messageParcel.writeInt(this.num)
91        messageParcel.writeString(this.str)
92        return true
93    }
94
95    unmarshalling(messageParcel) {
96        this.num = messageParcel.readInt()
97        this.str = messageParcel.readString()
98        return true
99    }
100}
101```
102**4. 实现Callee.on监听及Callee.off解除监听**
103
104  被调用端Callee的监听函数注册时机, 取决于应用开发者。注册监听之前的数据不会被处理,取消监听之后的数据不会被处理。如下示例在Ability的onCreate注册'MSG_SEND_METHOD'监听,在onDestroy取消监听,收到序列化数据后作相应处理并返回,应用开发者根据实际需要做相应处理。具体示例代码如下:
105```ts
106const TAG: string = '[CalleeAbility]'
107const MSG_SEND_METHOD: string = 'CallSendMsg'
108
109function sendMsgCallback(data) {
110    console.log('CalleeSortFunc called')
111
112    // 获取Caller发送的序列化数据
113    let receivedData = new MySequenceable(0, '')
114    data.readSequenceable(receivedData)
115    console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`)
116
117    // 作相应处理
118    // 返回序列化数据result给Caller
119    return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`)
120}
121
122export default class CalleeAbility extends Ability {
123    onCreate(want, launchParam) {
124        try {
125            this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
126        } catch (error) {
127            console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
128        }
129    }
130
131    onDestroy() {
132        try {
133            this.callee.off(MSG_SEND_METHOD)
134        } catch (error) {
135            console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`)
136        }
137    }
138}
139```
140
141### 访问Callee被调用端
142**1. 导入Ability模块**
143```ts
144import Ability from '@ohos.app.ability.UIAbility'
145```
146**2. 获取Caller通信接口**
147
148  Ability的context属性实现了startAbilityByCall方法,用于获取指定通用组件的Caller通信接口。如下示例通过`this.context`获取Ability实例的context属性,使用startAbilityByCall拉起Callee被调用端并获取Caller通信接口,注册Caller的onRelease监听。应用开发者根据实际需要做相应处理。具体示例代码如下:
149```ts
150// 注册caller的release监听
151private regOnRelease(caller) {
152    try {
153        caller.on("release", (msg) => {
154            console.log(`caller onRelease is called ${msg}`)
155        })
156        console.log('caller register OnRelease succeed')
157    } catch (error) {
158        console.log(`caller register OnRelease failed with ${error}`)
159    }
160}
161
162async onButtonGetCaller() {
163    try {
164        this.caller = await context.startAbilityByCall({
165            bundleName: 'com.samples.CallApplication',
166            abilityName: 'CalleeAbility'
167        })
168        if (this.caller === undefined) {
169            console.log('get caller failed')
170            return
171        }
172        console.log('get caller success')
173        this.regOnRelease(this.caller)
174    } catch (error) {
175        console.log(`get caller failed with ${error}`)
176    }
177}
178```
179  在跨设备场景下,需指定对端设备deviceId。具体示例代码如下:
180```ts
181async onButtonGetRemoteCaller() {
182    var caller = undefined
183    var context = this.context
184
185    context.startAbilityByCall({
186        deviceId: getRemoteDeviceId(),
187        bundleName: 'com.samples.CallApplication',
188        abilityName: 'CalleeAbility'
189    }).then((data) => {
190        if (data != null) {
191            caller = data
192            console.log('get remote caller success')
193            // 注册caller的release监听
194            caller.on("release", (msg) => {
195                console.log(`remote caller onRelease is called ${msg}`)
196            })
197            console.log('remote caller register OnRelease succeed')
198        }
199    }).catch((error) => {
200        console.error(`get remote caller failed with ${error}`)
201    })
202}
203```
204  从DeviceManager获取指定设备的deviceId,getTrustedDeviceListSync接口仅对系统应用开放。具体示例代码如下:
205```ts
206import deviceManager from '@ohos.distributedHardware.deviceManager';
207var dmClass;
208function getRemoteDeviceId() {
209    if (typeof dmClass === 'object' && dmClass != null) {
210        var list = dmClass.getTrustedDeviceListSync()
211        if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
212            console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null")
213            return
214        }
215        console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId)
216        return list[0].deviceId
217    } else {
218        console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null")
219    }
220}
221```
222  在跨设备场景下,需要向用户申请数据同步的权限。具体示例代码如下:
223```ts
224import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts';
225
226requestPermission() {
227    let context = this.context
228    let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
229    let atManager = abilityAccessCtrl.createAtManager();
230    atManager.requestPermissionsFromUser(context, permissions).then((data) => {
231        console.log("Succeed to request permission from user with data: "+ JSON.stringify(data))
232    }).catch((error) => {
233        console.log("Failed to request permission from user with error: "+ JSON.stringify(error))
234    })
235}
236```
237**3. 发送约定序列化数据**
238
239  向被调用端发送Sequenceable数据有两种方式,一种是不带返回值,一种是获取被调用端返回的数据,method以及序列化数据需要与被调用端协商一致。如下示例调用Call接口,向Callee被调用端发送数据。具体示例代码如下:
240```ts
241const MSG_SEND_METHOD: string = 'CallSendMsg'
242async onButtonCall() {
243    try {
244        let msg = new MySequenceable(1, 'origin_Msg')
245        await this.caller.call(MSG_SEND_METHOD, msg)
246    } catch (error) {
247        console.log(`caller call failed with ${error}`)
248    }
249}
250```
251
252  如下示例调用CallWithResult接口,向Callee被调用端发送待处理的数据`originMsg`,并将'CallSendMsg'方法处理完毕的数据赋值给`backMsg`。具体示例代码如下:
253```ts
254const MSG_SEND_METHOD: string = 'CallSendMsg'
255originMsg: string = ''
256backMsg: string = ''
257async onButtonCallWithResult(originMsg, backMsg) {
258    try {
259        let msg = new MySequenceable(1, originMsg)
260        const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg)
261        console.log('caller callWithResult succeed')
262
263        let result = new MySequenceable(0, '')
264        data.readSequenceable(result)
265        backMsg(result.str)
266        console.log(`caller result is [${result.num}, ${result.str}]`)
267    } catch (error) {
268        console.log(`caller callWithResult failed with ${error}`)
269    }
270}
271```
272**4. 释放Caller通信接口**
273
274  Caller不再使用后,应用开发者可以通过release接口释放Caller。具体示例代码如下:
275```ts
276releaseCall() {
277    try {
278        this.caller.release()
279        this.caller = undefined
280        console.log('caller release succeed')
281    } catch (error) {
282        console.log(`caller release failed with ${error}`)
283    }
284}
285```