• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ServiceAbility开发指导
2
3## 场景介绍
4基于Service模板的Ability(以下简称“Service”)主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动。即使用户切换到其他应用,Service仍将在后台继续运行。
5
6## 生命周期
7
8**表1** Service中相关生命周期API功能介绍
9|接口名|描述|
10|:------|:------|
11|onStart?(): void|该方法在创建Service的时候调用,用于Service的初始化,在Service的整个生命周期只会调用一次。|
12|onCommand?(want: Want, startId: number): void|在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,开发者可以在该方法中做一些调用统计、初始化类的操作。|
13|onConnect?(want: Want): rpc.RemoteObject|在Ability和Service连接时调用。|
14|onDisconnect?(want: Want): void|在Ability与绑定的Service断开连接时调用。|
15|onStop?(): void|在Service销毁时调用。开发者应通过实现此方法来清理资源,如关闭线程、注册的侦听器等。|
16
17onCommand()与onConnect()的区别在于:
18 - onCommand()只能被startAbility或startAbilityForResult触发,客户端每次启动Service均会触发该回调
19 - onConnect()只能被connectAbility触发,客户端每次与Servcie建立新的连接时会触发该回调
20
21## 开发步骤
22
23### 创建注册Service
24
251. 重写Service的生命周期方法,来添加其他Ability请求与Service Ability交互时的处理方法。
26
27   创建Service的代码示例如下:
28
29   ```ts
30    export default {
31        onStart() {
32            console.log('ServiceAbility onStart');
33        },
34        onCommand(want, startId) {
35            console.log('ServiceAbility onCommand');
36        },
37        onConnect(want) {
38            console.log('ServiceAbility OnConnect');
39            // ServiceAbilityStub的实现在下文给出
40            return new ServiceAbilityStub('test');
41        },
42        onDisconnect(want) {
43            console.log('ServiceAbility OnDisConnect');
44        },
45        onStop() {
46            console.log('ServiceAbility onStop');
47        }
48    }
49   ```
50
512. 注册Service。
52
53   Service需要在应用配置文件config.json中进行注册,注册类型type需要设置为service。
54
55   ```json
56    {
57      "module": {
58        "abilities": [
59          {
60            "name": ".ServiceAbility",
61            "type": "service",
62            "visible": true
63            ...
64          }
65        ]
66        ...
67      }
68      ...
69    }
70   ```
71
72
73
74### 启动Service
75
76Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Want传递给该方法来启动Service。
77
78开发者可以通过构造包含bundleName与abilityName的Want对象来设置目标Service信息。参数的含义如下:
79
80- bundleName:表示对端应用的包名称。
81- abilityName:表示待启动的Ability名称。
82
83启动本地设备Service的代码示例如下:
84
85```ts
86import featureAbility from '@ohos.ability.featureAbility'
87
88featureAbility.startAbility(
89    {
90        want:
91        {
92            bundleName: "com.jstest.service",
93            abilityName: "com.jstest.service.ServiceAbility"
94        }
95    }
96).then((err) => {
97    console.log("startService success");
98}).catch (err => {
99    console.log("startService FAILED");
100});
101```
102
103执行上述代码后,Ability将通过startAbility() 方法来启动Service。
104- 如果Service尚未运行,则系统会先初始化Service,然后回调onStart()来启动Service,再回调onCommand()方法。
105- 如果Service正在运行,则系统会直接回调Service的onCommand()方法。
106
107启动远端设备Service的代码示例如下,详见[连接远程Service](fa-serviceability.md#连接远程service当前仅对系统应用开放):
108
109```ts
110import featureAbility from '@ohos.ability.featureAbility'
111
112featureAbility.startAbility(
113    {
114        want:
115        {
116            deviceId: remoteDeviceId,    // 远端设备Id
117            bundleName: "com.jstest.service",
118            abilityName: "com.jstest.service.ServiceAbility"
119        }
120    }
121).then((err) => {
122    console.log("startService success");
123}).catch (err => {
124    console.log("startService FAILED");
125});
126```
127
128
129### 停止Service
130
131  常规情况下,Service可以将自己停止,或者被系统停止,具体场景如下:
132   - Service调用particleAbility.terminateSelf()方法将自己停止。
133   - Service所在的应用进程退出,Service将随着进程被回收。
134   - 若Service仅仅是通过connectAbility()方法被访问的(从未执行过onCommand()回调),那么当最后一个连接被断开后,系统会将Service停止。
135
136### 连接本地Service
137
138如果Service需要与Page Ability或其他应用的Service Ability进行交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。
139
140
141开发者可使用如下两种方式实现连接Service。
142
1431. 使用IDL自动生成代码
144
145    使用OpenHarmony IDL(OpenHarmony Interface Definition Language)来自动生成对应客户端服务端及IRemoteObject代码,具体示例代码和说明请参考:
146
147   - [`OpenHarmony IDL`:TS开发步骤](../IDL/idl-guidelines.md#ts)
148
1492. 在对应文件编写代码
150
151    在使用connectAbility()时,需要传入目标Service的Want与ConnectOptions的实例,其中ConnectOptions封装了三个回调,分别对应不同情况,开发者需自行实现:
152     - onConnect():用来处理连接Service成功的回调。
153     - onDisconnect():用来处理Service断连或异常死亡的回调。
154     - onFailed():用来处理连接Service失败的回调。
155
156    创建连接本地Service回调实例的代码示例如下:
157
158    ```ts
159    import prompt from '@system.prompt'
160
161    var option = {
162        onConnect: function onConnectCallback(element, proxy) {
163            console.log(`onConnectLocalService onConnectDone`);
164            if (proxy === null) {
165                prompt.showToast({
166                    message: "Connect service failed"
167                });
168                return;
169            }
170            // 得到Service的proxy对象后便可以与其进行通信
171            let data = rpc.MessageParcel.create();
172            let reply = rpc.MessageParcel.create();
173            let option = new rpc.MessageOption();
174            data.writeString("InuptString");
175            proxy.sendRequest(0, data, reply, option);
176            prompt.showToast({
177                message: "Connect service success"
178            });
179        },
180        onDisconnect: function onDisconnectCallback(element) {
181            console.log(`onConnectLocalService onDisconnectDone element:${element}`);
182            prompt.showToast({
183                message: "Disconnect service success"
184            });
185        },
186        onFailed: function onFailedCallback(code) {
187            console.log(`onConnectLocalService onFailed errCode:${code}`);
188            prompt.showToast({
189                message: "Connect local service onFailed"
190            });
191        }
192    };
193    ```
194
195    连接本地Service的代码示例如下:
196
197    ```ts
198    import featureAbility from '@ohos.ability.featureAbility'
199
200    let want = {
201        bundleName: "com.jstest.service",
202        abilityName: "com.jstest.service.ServiceAbility"
203    };
204    let connectId = featureAbility.connectAbility(want, option);
205    ```
206
207    同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象。OpenHarmony提供了IRemoteObject的默认实现,开发者可以通过继承rpc.RemoteObject来创建自定义的实现类,从而实现与Service的通信。具体使用方法可参考[ohos.rpc API文档](..\reference\apis\js-apis-rpc.md)。
208
209    Service侧把自身的实例返回给调用侧的代码示例如下:
210
211    ```ts
212    import rpc from "@ohos.rpc"
213
214    class ServiceAbilityStub extends rpc.RemoteObject {
215        constructor(des: any) {
216            if (typeof des === 'string') {
217                super(des);
218            } else {
219                console.log("Error, the input param is not string");
220                return;
221            }
222        }
223
224        onRemoteRequest(code: number, data: any, reply: any, option: any) {
225            console.log("onRemoteRequest called");
226            // 可根据code执行不同的业务逻辑
227            if (code === 1) {
228                // 将传入的字符串进行排序
229                let string = data.readString();
230                console.log(`Input string = ${string}`);
231                let result = Array.from(string).sort().join('');
232                console.log(`Output result = ${result}`);
233                reply.writeString(result);
234            } else {
235                console.log(`Unknown request code`);
236            }
237            return true;
238        }
239    }
240
241    export default {
242        onStart() {
243            console.log('ServiceAbility onStart');
244        },
245        onCommand(want, startId) {
246            console.log('ServiceAbility onCommand');
247        },
248        onConnect(want) {
249            console.log('ServiceAbility OnConnect');
250            return new ServiceAbilityStub('ServiceAbilityRemoteObject');
251        },
252        onDisconnect(want) {
253            console.log('ServiceAbility OnDisConnect');
254        },
255        onStop() {
256            console.log('ServiceAbility onStop');
257        }
258    }
259    ```
260
261### 连接远程Service(当前仅对系统应用开放)
262
263连接远程Service,构造ConnectOptions的方法与连接本地Serivce相同,区别在于:
264 - 应用需要向用户申请数据同步权限
265 - 目标Service的Want需要包含对端设备的deviceId
266
267> 说明:
268> (1) 由于DeviceManager的getTrustedDeviceList等接口仅对系统应用开放,当前仅系统应用支持连接远程Service。
269> (2) API定义可见:[deviceManager模块](..\reference\apis\js-apis-device-manager.md)
270
271在跨设备场景下,需要向用户申请数据同步的权限,首先在config.json里配置权限:
272
273```json
274{
275  ...
276  "module": {
277    ...
278    "reqPermissions": [{
279      "name": "ohos.permission.DISTRIBUTED_DATASYNC"
280    }]
281  }
282}
283```
284
285DISTRIBUTED_DATASYNC权限需要用户授予,在应用启动时需要向用户弹框请求授予权限,示例代码如下:
286
287```ts
288import abilityAccessCtrl from "@ohos.abilityAccessCtrl"
289import bundle from '@ohos.bundle'
290
291async function RequestPermission() {
292    console.info('RequestPermission begin');
293    let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"];
294    let bundleFlag = 0;
295    let tokenID = undefined;
296    let userID = 100;
297    let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID);
298    tokenID = appInfo.accessTokenId;
299    let atManager = abilityAccessCtrl.createAtManager();
300    let requestPermissions: Array<string> = [];
301    for (let i = 0;i < array.length; i++) {
302        let result = await atManager.verifyAccessToken(tokenID, array[i]);
303        console.info("verifyAccessToken result:" + JSON.stringify(result));
304        if (result != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
305            requestPermissions.push(array[i]);
306        }
307    }
308    console.info("requestPermissions:" + JSON.stringify(requestPermissions));
309    if (requestPermissions.length == 0 || requestPermissions == []) {
310        return;
311    }
312    let context = featureAbility.getContext();
313    context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{
314        console.info("data:" + JSON.stringify(data));
315    });
316    console.info('RequestPermission end');
317}
318```
319
320获取deviceId需要导入`@ohos.distributedHardware.deviceManager`模块,其中提供了getTrustedDeviceList等接口用于获取远端设备的deviceId。
321 - 接口使用可参考[deviceManager模块](..\reference\apis\js-apis-device-manager.md)
322
323连接远程Service,只需要在want内定义deviceId即可,示例代码如下:
324
325```ts
326import featureAbility from '@ohos.ability.featureAbility'
327
328let want = {
329    deviceId: remoteDeviceId,
330    bundleName: "com.jstest.service",
331    abilityName: "com.jstest.service.ServiceAbility"
332};
333let connectId = featureAbility.connectAbility(want, option);
334```
335
336其余实现均与本地连接Service相同,参考[连接本地Service](fa-serviceability.md#连接本地service)的示例代码即可。
337
338