• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ServiceExtensionAbility
2
3## 概述
4
5[ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md)是SERVICE类型的[ExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-extensionAbility.md)组件,提供后台服务能力,其内部持有了一个[ServiceExtensionContext](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md),通过ServiceExtensionContext提供了丰富的接口供外部使用。
6
7本文描述中称被启动的ServiceExtensionAbility为服务端,称启动ServiceExtensionAbility的组件为客户端。
8
9ServiceExtensionAbility可以被其他组件启动或连接,并根据调用者的请求信息在后台处理相关事务。ServiceExtensionAbility支持以启动和连接两种形式运行,系统应用可以调用[startServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstartserviceextensionability)方法启动后台服务,也可以调用[connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)方法连接后台服务,而三方应用只能调用connectServiceExtensionAbility()方法连接后台服务。启动和连接后台服务的差别:
10
11- **启动**:AbilityA启动ServiceB,启动后AbilityA和ServiceB为弱关联,AbilityA退出后,ServiceB可以继续存在。
12
13- **连接**:AbilityA连接ServiceB,连接后AbilityA和ServiceB为强关联,AbilityA退出后,ServiceB也一起退出。
14
15此处有如下细节需要注意:
16
17- 若Service只通过connect的方式被拉起,那么该Service的生命周期将受客户端控制,当客户端调用一次connectServiceExtensionAbility()方法,将建立一个连接,当客户端退出或者调用[disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability)方法,该连接将断开。当所有连接都断开后,Service将自动退出。
18
19- Service一旦通过start的方式被拉起,将不会自动退出,系统应用可以调用[stopServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstopserviceextensionability)方法将Service退出。
20
21- 只能在主线程线程中执行connect/disconnect操作,不要在Worker、TaskPool等子线程中执行connect/disconnect操作。
22
23> **说明:**
24>
25> 1. 当前不支持三方应用实现ServiceExtensionAbility。如果三方开发者想要实现后台处理相关事务的功能,可以使用后台任务,具体请参见[后台任务](../task-management/background-task-overview.md)。
26> 2. 三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
27> 3. 三方应用需要在前台获焦的情况下才能连接系统提供的ServiceExtensionAbility。
28
29## 生命周期
30
31[ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md)提供了[onCreate()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityoncreate)、[onRequest()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityonrequest)、[onConnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityonconnect)、[onDisconnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityondisconnect)和[onDestroy()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityondestroy)生命周期回调,根据需要重写对应的回调方法。下图展示了ServiceExtensionAbility的生命周期。
32
33  **图1** ServiceExtensionAbility生命周期
34![ServiceExtensionAbility-lifecycle](figures/ServiceExtensionAbility-lifecycle.png)
35
36- **onCreate**
37  服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。
38
39  > **说明:**
40  > 如果服务已创建,再次启动该ServiceExtensionAbility不会触发onCreate()回调。
41
42- **onRequest**
43  当另一个组件调用[startServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstartserviceextensionability)方法启动该服务组件时,触发该回调。执行此方法后,服务会启动并在后台运行。每调用一次startServiceExtensionAbility()方法均会触发该回调。
44
45- **onConnect**
46  当另一个组件调用[connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)方法与该服务连接时,触发该回调。开发者在此方法中,返回一个远端代理对象([IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject)),客户端拿到这个对象后可以通过这个对象与服务端进行RPC通信,同时系统侧也会将该远端代理对象(IRemoteObject)储存。后续若有组件再调用connectServiceExtensionAbility()方法,系统侧会直接将所保存的远端代理对象(IRemoteObject)返回,而不再触发该回调。
47
48- **onDisconnect**
49  当最后一个连接断开时,将触发该回调。客户端死亡或者调用[disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability)方法可以使连接断开。
50
51- **onDestroy**
52  当不再使用服务且准备将其销毁该实例时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。
53
54## 实现一个后台服务(仅对系统应用开放)
55
56### 开发准备
57
58只有系统应用才允许实现[ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md),因此开发者在开发之前需做如下准备:
59
60- **替换Full SDK**:ServiceExtensionAbility相关接口都被标记为System-API,默认对开发者隐藏,因此需要手动从镜像站点获取Full SDK,并在DevEco Studio中替换,具体操作可参考[替换指南](../faqs/full-sdk-switch-guide.md)。
61
62- **申请AllowAppUsePrivilegeExtension特权**:只有具有AllowAppUsePrivilegeExtension特权的应用才允许开发ServiceExtensionAbility,具体申请方式可参考[应用特权配置指南](../../device-dev/subsystems/subsys-app-privilege-config-guide.md)。
63
64### 定义IDL接口
65
66ServiceExtensionAbility作为后台服务,需要向外部提供可调用的接口,开发者可将接口定义在idl文件中,并使用[IDL工具](../IDL/idl-guidelines.md)生成对应的proxy、stub文件。此处定义一个名为IIdlServiceExt.idl的文件作为示例:
67
68```cpp
69interface OHOS.IIdlServiceExt {
70  int ProcessData([in] int data);
71  void InsertDataToMap([in] String key, [in] int val);
72}
73```
74
75在DevEco Studio工程Module对应的ets目录下手动新建名为IdlServiceExt的目录,将[IDL工具](../IDL/idl-guidelines.md)生成的文件复制到该目录下,并创建一个名为idl_service_ext_impl.ts的文件,作为idl接口的实现:
76
77```
78├── ets
79│ ├── IdlServiceExt
80│ │   ├── i_idl_service_ext.ts      # 生成文件
81│ │   ├── idl_service_ext_proxy.ts  # 生成文件
82│ │   ├── idl_service_ext_stub.ts   # 生成文件
83│ │   ├── idl_service_ext_impl.ts   # 开发者自定义文件,对idl接口的具体实现
84│ └
8586```
87
88idl_service_ext_impl.ts实现如下:
89
90```ts
91import IdlServiceExtStub from './idl_service_ext_stub';
92import hilog from '@ohos.hilog';
93import type { insertDataToMapCallback } from './i_idl_service_ext';
94import type { processDataCallback } from './i_idl_service_ext';
95
96const ERR_OK = 0;
97const TAG: string = "[IdlServiceExtImpl]";
98const DOMAIN_NUMBER: number = 0xFF00;
99
100// 开发者需要在这个类型里对接口进行实现
101export default class ServiceExtImpl extends IdlServiceExtStub {
102  processData(data: number, callback: processDataCallback): void {
103    // 开发者自行实现业务逻辑
104    hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
105    callback(ERR_OK, data + 1); // 鉴权通过,执行正常业务逻辑
106  }
107
108  insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void {
109    // 开发者自行实现业务逻辑
110    hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
111    callback(ERR_OK);
112  }
113}
114```
115
116### 创建ServiceExtensionAbility
117
118在DevEco Studio工程中手动新建一个ServiceExtensionAbility,具体步骤如下:
119
1201. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为ServiceExtAbility。
121
1222. 在ServiceExtAbility目录,右键选择“New > ArkTS File”,新建一个文件并命名为ServiceExtAbility.ets123
124    ```
125    ├── ets
126    │ ├── IdlServiceExt
127    │ │   ├── i_idl_service_ext.ets      # 生成文件
128    │ │   ├── idl_service_ext_proxy.ets  # 生成文件
129    │ │   ├── idl_service_ext_stub.ets   # 生成文件
130    │ │   ├── idl_service_ext_impl.ets   # 开发者自定义文件,对idl接口的具体实现
131    │ ├── ServiceExtAbility
132    │ │   ├── ServiceExtAbility.ets
133134    ```
135
1363. 在ServiceExtAbility.ets文件中,增加导入[ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md)的依赖包,自定义类继承ServiceExtensionAbility并实现生命周期回调,在[onConnect](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityoncreate)生命周期回调里,需要将之前定义的ServiceExtImpl对象返回。
137
138    ```ts
139    import { ServiceExtensionAbility, Want } from '@kit.AbilityKit';
140    import { rpc } from '@kit.IPCKit';
141    import { hilog } from '@kit.PerformanceAnalysisKit';
142    import ServiceExtImpl from '../IdlServiceExt/idl_service_ext_impl';
143
144    const TAG: string = '[ServiceExtAbility]';
145    const DOMAIN_NUMBER: number = 0xFF00;
146
147    export default class ServiceExtAbility extends ServiceExtensionAbility {
148      serviceExtImpl: ServiceExtImpl = new ServiceExtImpl('ExtImpl');
149
150      onCreate(want: Want): void {
151        let serviceExtensionContext = this.context;
152        hilog.info(DOMAIN_NUMBER, TAG, `onCreate, want: ${want.abilityName}`);
153      };
154
155      onRequest(want: Want, startId: number): void {
156        hilog.info(DOMAIN_NUMBER, TAG, `onRequest, want: ${want.abilityName}`);
157      };
158
159      onConnect(want: Want): rpc.RemoteObject {
160        hilog.info(DOMAIN_NUMBER, TAG, `onConnect, want: ${want.abilityName}`);
161        // 返回ServiceExtImpl对象,客户端获取后便可以与ServiceExtensionAbility进行通信
162        return this.serviceExtImpl as rpc.RemoteObject;
163      };
164
165      onDisconnect(want: Want): void {
166        hilog.info(DOMAIN_NUMBER, TAG, `onDisconnect, want: ${want.abilityName}`);
167      };
168
169      onDestroy(): void {
170        hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy');
171      };
172    };
173    ```
174
1754. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册ServiceExtensionAbility,type标签需要设置为“service”,srcEntry标签表示当前ExtensionAbility组件所对应的代码路径。
176
177    ```json
178    {
179      "module": {
180        // ...
181        "extensionAbilities": [
182          {
183            "name": "ServiceExtAbility",
184            "icon": "$media:icon",
185            "description": "service",
186            "type": "service",
187            "exported": true,
188            "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ets"
189          }
190        ]
191      }
192    }
193    ```
194
195## 启动一个后台服务(仅对系统应用开放)
196
197系统应用通过[startServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstartserviceextensionability)方法启动一个后台服务,服务的[onRequest()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityonrequest)回调就会被调用,并在该回调方法中接收到调用者传递过来的[Want](../reference/apis-ability-kit/js-apis-app-ability-want.md)对象。后台服务启动后,其生命周期独立于客户端,即使客户端已经销毁,该后台服务仍可继续运行。因此,后台服务需要在其工作完成时通过调用[ServiceExtensionContext](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md)的[terminateSelf()](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md#serviceextensioncontextterminateself)来自行停止,或者由另一个组件调用[stopServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstopserviceextensionability)来将其停止。
198
199> **说明:**
200> ServiceExtensionContext的startServiceExtensionAbility()、stopServiceExtensionAbility()和terminateSelf()为系统接口,三方应用不支持调用。
201
2021. 在系统应用中启动一个新的[ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md)。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
203
204    ```ts
205    import { common, Want } from '@kit.AbilityKit';
206    import { hilog } from '@kit.PerformanceAnalysisKit';
207    import { BusinessError } from '@kit.BasicServicesKit';
208
209    const TAG: string = '[Page_ServiceExtensionAbility]';
210    const DOMAIN_NUMBER: number = 0xFF00;
211
212    @Entry
213    @Component
214    struct Page_ServiceExtensionAbility {
215      build() {
216        Column() {
217          //...
218          List({ initialIndex: 0 }) {
219            ListItem() {
220              Row() {
221                //...
222              }
223              .onClick(() => {
224                let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
225                let want: Want = {
226                  deviceId: '',
227                  bundleName: 'com.samples.stagemodelabilitydevelop',
228                  abilityName: 'ServiceExtAbility'
229                };
230                context.startServiceExtensionAbility(want).then(() => {
231                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ServiceExtensionAbility.');
232                  // 成功启动后台服务
233                  this.getUIContext().getPromptAction().showToast({
234                    message: 'SuccessfullyStartBackendService'
235                  });
236                }).catch((err: BusinessError) => {
237                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
238                });
239              })
240            }
241            //...
242          }
243          //...
244        }
245        //...
246      }
247    }
248    ```
249
2502. 在系统应用中停止一个已启动的[ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md)。
251
252    ```ts
253    import { common, Want } from '@kit.AbilityKit';
254    import { hilog } from '@kit.PerformanceAnalysisKit';
255    import { BusinessError } from '@kit.BasicServicesKit';
256
257    const TAG: string = '[Page_ServiceExtensionAbility]';
258    const DOMAIN_NUMBER: number = 0xFF00;
259
260    @Entry
261    @Component
262    struct Page_ServiceExtensionAbility {
263      build() {
264        Column() {
265          //...
266          List({ initialIndex: 0 }) {
267            ListItem() {
268              Row() {
269                //...
270              }
271              .onClick(() => {
272                let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
273                let want: Want = {
274                  deviceId: '',
275                  bundleName: 'com.samples.stagemodelabilitydevelop',
276                  abilityName: 'ServiceExtAbility'
277                };
278                context.stopServiceExtensionAbility(want).then(() => {
279                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping ServiceExtensionAbility.');
280                  this.getUIContext().getPromptAction().showToast({
281                    message: 'SuccessfullyStoppedAStartedBackendService'
282                  });
283                }).catch((err: BusinessError) => {
284                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
285                });
286              })
287            }
288            //...
289          }
290          //...
291        }
292        //...
293      }
294    }
295    ```
296
2973. 已启动的[ServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md)停止自身。
298
299    ```ts
300    import { common } from '@kit.AbilityKit';
301    import { hilog } from '@kit.PerformanceAnalysisKit';
302    import { BusinessError } from '@kit.BasicServicesKit';
303
304    const TAG: string = '[Page_ServiceExtensionAbility]';
305    const DOMAIN_NUMBER: number = 0xFF00;
306
307    @Entry
308    @Component
309    struct Page_ServiceExtensionAbility {
310      build() {
311        Column() {
312          //...
313          List({ initialIndex: 0 }) {
314            ListItem() {
315              Row() {
316                //...
317              }
318              .onClick(() => {
319                let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
320                context.terminateSelf().then(() => {
321                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in terminating self.');
322                  // 成功停止当前后台服务
323                  this.getUIContext().getPromptAction().showToast({
324                    message: 'SuccessfullyStopStartedBackendService'
325                  });
326                }).catch((err: BusinessError) => {
327                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
328                });
329              })
330            }
331            //...
332          }
333          //...
334        }
335        //...
336      }
337    }
338    ```
339
340> **说明:**
341> 后台服务可以在后台长期运行,为了避免资源浪费,需要对后台服务的生命周期进行管理。即一个后台服务完成了请求方的任务,需要及时销毁。销毁已启动的后台服务有两种方式:
342>
343> - 后台服务自身调用[terminateSelf()](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md#serviceextensioncontextterminateself)方法来自行停止。
344> - 由其他组件调用[stopServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext-sys.md#uiabilitycontextstopserviceextensionability)方法来停止。
345> 调用terminateSelf()或stopServiceExtensionAbility()方法之后,系统将销毁后台服务。
346
347## 连接一个后台服务
348
349系统应用或者三方应用可以通过[connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)连接一个服务(在Want对象中指定启动的目标服务),服务的[onConnect()](../reference/apis-ability-kit/js-apis-app-ability-serviceExtensionAbility-sys.md#serviceextensionabilityonconnect)就会被调用,并在该回调方法中接收到调用者传递过来的[Want](../reference/apis-ability-kit/js-apis-app-ability-want.md)对象,从而建立长连接。
350
351ServiceExtensionAbility服务组件在onConnect()中返回[IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject)对象,开发者通过该IRemoteObject定义通信接口,用于客户端与服务端进行RPC交互。多个客户端可以同时连接到同一个后台服务,客户端完成与服务的交互后,客户端需要通过调用[disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability)来断开连接。如果所有连接到某个后台服务的客户端均已断开连接,则系统会销毁该服务。
352
353- 使用[connectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability)建立与后台服务的连接。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。
354
355  ```ts
356  import { common, Want } from '@kit.AbilityKit';
357  import { rpc } from '@kit.IPCKit';
358  import { hilog } from '@kit.PerformanceAnalysisKit';
359  // 客户端需要将服务端对外提供的idl_service_ext_proxy.ts导入到本地工程中
360  import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';
361
362  const TAG: string = '[Page_ServiceExtensionAbility]';
363  const DOMAIN_NUMBER: number = 0xFF00;
364
365  let connectionId: number;
366  let want: Want = {
367    deviceId: '',
368    bundleName: 'com.samples.stagemodelabilitydevelop',
369    abilityName: 'ServiceExtAbility'
370  };
371
372  let options: common.ConnectOptions = {
373    onConnect(elementName, remote: rpc.IRemoteObject): void {
374      hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
375      if (remote === null) {
376        hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
377        return;
378      }
379      let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);
380      // 通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了
381      serviceExtProxy.processData(1, (errorCode: number, retVal: number) => {
382        hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
383      });
384      serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => {
385        hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);
386      })
387    },
388    onDisconnect(elementName): void {
389      hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
390    },
391    onFailed(code: number): void {
392      hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));
393    }
394  };
395  @Entry
396  @Component
397  struct Page_ServiceExtensionAbility {
398    build() {
399      Column() {
400        //...
401        List({ initialIndex: 0 }) {
402          ListItem() {
403            Row() {
404              //...
405            }
406            .onClick(() => {
407              let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
408              // 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
409              connectionId = context.connectServiceExtensionAbility(want, options);
410              // 成功连接后台服务
411              this.getUIContext().getPromptAction().showToast({
412                message: 'SuccessfullyConnectBackendService'
413              });
414              // connectionId = context.connectAbility(want, options);
415              hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`);
416            })
417          }
418          //...
419        }
420        //...
421      }
422      //...
423    }
424  }
425  ```
426
427- 使用[disconnectServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability)断开与后台服务的连接。
428
429  ```ts
430  import { common } from '@kit.AbilityKit';
431  import { hilog } from '@kit.PerformanceAnalysisKit';
432  import { BusinessError } from '@kit.BasicServicesKit';
433
434  const TAG: string = '[Page_ServiceExtensionAbility]';
435  const DOMAIN_NUMBER: number = 0xFF00;
436
437  let connectionId: number;
438  @Entry
439  @Component
440  struct Page_ServiceExtensionAbility {
441    build() {
442      Column() {
443        //...
444        List({ initialIndex: 0 }) {
445          ListItem() {
446            Row() {
447              //...
448            }
449            .onClick(() => {
450              let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
451              // connectionId为调用connectServiceExtensionAbility接口时的返回值,需开发者自行维护
452              context.disconnectServiceExtensionAbility(connectionId).then(() => {
453                hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success');
454                // 成功断连后台服务
455                this.getUIContext().getPromptAction().showToast({
456                  message: 'SuccessfullyDisconnectBackendService'
457                });
458              }).catch((error: BusinessError) => {
459                hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed');
460              });
461            })
462          }
463          //...
464        }
465        //...
466      }
467      //...
468    }
469  }
470  ```
471
472## 客户端与服务端通信
473
474客户端在[onConnect()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#onconnect)中获取到[rpc.IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject)对象后便可与Service进行通信,有如下两种方式:
475
476- 使用服务端提供的IDL接口进行通信(推荐)。
477
478  ```ts
479  // 客户端需要将服务端对外提供的idl_service_ext_proxy.ts导入到本地工程中
480  import { common } from '@kit.AbilityKit';
481  import { rpc } from '@kit.IPCKit';
482  import { hilog } from '@kit.PerformanceAnalysisKit';
483  import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';
484
485  const TAG: string = '[Page_ServiceExtensionAbility]';
486  const DOMAIN_NUMBER: number = 0xFF00;
487
488  let options: common.ConnectOptions = {
489    onConnect(elementName, remote: rpc.IRemoteObject): void {
490      hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
491      if (remote === null) {
492        hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
493        return;
494      }
495      let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);
496      // 通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了
497      serviceExtProxy.processData(1, (errorCode: number, retVal: number) => {
498        hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
499      });
500      serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => {
501        hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);
502      })
503    },
504    onDisconnect(elementName): void {
505      hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
506    },
507    onFailed(code: number): void {
508      hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));
509    }
510  };
511  ```
512
513- 直接使用[sendMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#sendmessagerequest9)接口向服务端发送消息(不推荐)。
514
515  ```ts
516  import { common } from '@kit.AbilityKit';
517  import { promptAction } from '@kit.ArkUI';
518  import { rpc } from '@kit.IPCKit';
519  import { hilog } from '@kit.PerformanceAnalysisKit';
520  import { BusinessError } from '@kit.BasicServicesKit';
521
522  const TAG: string = '[Page_CollaborateAbility]';
523  const DOMAIN_NUMBER: number = 0xFF00;
524  const REQUEST_CODE = 1;
525  let options: common.ConnectOptions = {
526    onConnect(elementName, remote): void {
527      hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
528      if (remote === null) {
529        hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
530        return;
531      }
532      let option = new rpc.MessageOption();
533      let data = new rpc.MessageSequence();
534      let reply = new rpc.MessageSequence();
535
536      data.writeInt(99);
537      // 开发者可发送data到目标端应用进行相应操作
538      // @param code 表示客户端发送的服务请求代码。
539      // @param data 表示客户端发送的{@link MessageSequence}对象。
540      // @param reply 表示远程服务发送的响应消息对象。
541      // @param options 指示操作是同步的还是异步的。
542      // @return 如果操作成功返回{@code true}; 否则返回 {@code false}。
543
544      remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => {
545        let errCode = reply.readInt(); // 在成功连接的情况下,会收到来自目标端返回的信息(100)
546        let msg: number = 0;
547        if (errCode === 0) {
548          msg = reply.readInt();
549        }
550        hilog.info(DOMAIN_NUMBER, TAG, `sendRequest msg:${msg}`);
551        // 成功连接后台服务
552        promptAction.showToast({
553          message: `sendRequest msg:${msg}`
554        });
555      }).catch((error: BusinessError) => {
556        hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`);
557      });
558    },
559    onDisconnect(elementName): void {
560      hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
561    },
562    onFailed(code): void {
563      hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback');
564    }
565  };
566  //...
567  ```
568
569## 服务端对客户端身份校验
570
571部分开发者需要使用ServiceExtension提供一些较为敏感的服务,因此需要对客户端身份进行校验,开发者可在IDL接口的stub端进行校验,IDL接口实现详见上文[定义IDL接口](#定义idl接口),此处推荐两种校验方式:
572
573- **通过callerUid识别客户端应用**
574
575  通过调用[getCallingUid()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallinguid)接口获取客户端的uid,再调用[getBundleNameByUid()](../reference/apis-ability-kit/js-apis-bundleManager-sys.md#bundlemanagergetbundlenamebyuid)接口获取uid对应的bundleName,从而识别客户端身份。此处需要注意的是[getBundleNameByUid()](../reference/apis-ability-kit/js-apis-bundleManager-sys.md#bundlemanagergetbundlenamebyuid)是一个异步接口,因此服务端无法将校验结果返回给客户端,这种校验方式适合客户端向服务端发起执行异步任务请求的场景,示例代码如下:
576
577  ```ts
578  import { bundleManager } from '@kit.AbilityKit';
579  import { rpc } from '@kit.IPCKit';
580  import { hilog } from '@kit.PerformanceAnalysisKit';
581  import { BusinessError } from '@kit.BasicServicesKit';
582  import IdlServiceExtStub from './idl_service_ext_stub';
583  import type { InsertDataToMapCallback } from './i_idl_service_ext';
584  import type { ProcessDataCallback } from './i_idl_service_ext';
585
586  const ERR_OK = 0;
587  const ERR_DENY = -1;
588  const TAG: string = "[IdlServiceExtImpl]";
589  const DOMAIN_NUMBER: number = 0xFF00;
590
591  // 开发者需要在这个类型里对接口进行实现
592  export default class ServiceExtImpl extends IdlServiceExtStub {
593    processData(data: number, callback: ProcessDataCallback): void {
594      // 开发者自行实现业务逻辑
595      hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
596      let callerUid = rpc.IPCSkeleton.getCallingUid();
597      bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => {
598        hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);
599        // 对客户端包名进行识别
600        if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 识别不通过
601          hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');
602          return;
603        }
604        // 识别通过,执行正常业务逻辑
605      }).catch((err: BusinessError) => {
606        hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
607      });
608      //...
609    };
610
611    insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void {
612      // 开发者自行实现业务逻辑
613      hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
614      callback(ERR_OK);
615    };
616  };
617  ```
618
619- **通过callerTokenId对客户端进行鉴权**
620
621  通过调用[getCallingTokenId()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallingtokenid)接口获取客户端的tokenID,再调用[verifyAccessTokenSync()](../reference/apis-ability-kit/js-apis-abilityAccessCtrl.md#verifyaccesstokensync)接口判断客户端是否有某个具体权限,由于当前不支持自定义权限,因此只能校验当前[系统所定义的权限](../security/AccessToken/app-permissions.md)。示例代码如下:
622
623  ```ts
624  import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit';
625  import { rpc } from '@kit.IPCKit';
626  import { hilog } from '@kit.PerformanceAnalysisKit';
627  import { BusinessError } from '@kit.BasicServicesKit';
628  import IdlServiceExtStub from './idl_service_ext_stub';
629  import type { InsertDataToMapCallback } from './i_idl_service_ext';
630  import type { ProcessDataCallback } from './i_idl_service_ext';
631
632  const ERR_OK = 0;
633  const ERR_DENY = -1;
634  const TAG: string = '[IdlServiceExtImpl]';
635  const DOMAIN_NUMBER: number = 0xFF00;
636
637  // 开发者需要在这个类型里对接口进行实现
638  export default class ServiceExtImpl extends IdlServiceExtStub {
639    processData(data: number, callback: ProcessDataCallback): void {
640      // 开发者自行实现业务逻辑
641      hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
642
643      let callerUid = rpc.IPCSkeleton.getCallingUid();
644      bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => {
645        hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);
646        // 对客户端包名进行识别
647        if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 识别不通过
648          hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');
649          return;
650        }
651        // 识别通过,执行正常业务逻辑
652      }).catch((err: BusinessError) => {
653        hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
654      });
655
656      let callerTokenId = rpc.IPCSkeleton.getCallingTokenId();
657      let accessManger = abilityAccessCtrl.createAtManager();
658      // 所校验的具体权限由开发者自行选择,此处ohos.permission.GET_BUNDLE_INFO_PRIVILEGED只作为示例
659      let grantStatus = accessManger.verifyAccessTokenSync(callerTokenId, 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED');
660      if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
661        hilog.info(DOMAIN_NUMBER, TAG, 'PERMISSION_DENIED');
662        callback(ERR_DENY, data); // 鉴权失败,返回错误
663        return;
664      }
665      hilog.info(DOMAIN_NUMBER, TAG, 'verify access token success.');
666      callback(ERR_OK, data + 1); // 鉴权通过,执行正常业务逻辑
667    };
668
669    insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void {
670      // 开发者自行实现业务逻辑
671      hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
672      callback(ERR_OK);
673    };
674  };
675  ```
676
677## 相关实例
678
679针对ServiceExtensionAbility开发,有以下相关实例可供参考:
680
681- [Ability与ServiceExtensionAbility通信(ArkTS)(Full SDK)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/IDL/AbilityConnectServiceExtension)
682
683- [Stage模型(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/ApplicationModels/StageModel)
684