• 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的整个生命周期只会调用一次,调用时传入的Want应为空。|
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销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。|
16
17## 开发步骤
18
19### 创建Service
20
211. 重写Service的生命周期方法,来添加其他Ability请求与Service Ability交互时的处理方法。
22
23   创建Service的代码示例如下:
24
25   ```javascript
26   export default {
27       onStart() {
28           console.log('ServiceAbility onStart');
29       },
30       onCommand(want, startId) {
31           console.log('ServiceAbility onCommand');
32       },
33       onConnect(want) {
34           console.log('ServiceAbility OnConnect');
35           return null;
36       },
37       onDisconnect(want) {
38           console.log('ServiceAbility OnDisConnect');
39       },
40       onStop() {
41           console.log('ServiceAbility onStop');
42       },
43   }
44   ```
45
462. 注册Service。
47
48   Service需要在应用配置文件config.json中进行注册,注册类型type需要设置为service。
49
50   ```javascript
51    {
52        "module": {
53            "abilities": [
54                {
55                    "name": ".ServiceAbility",
56                    "type": "service",
57                    "visible": true
58                    ...
59                }
60            ]
61            ...
62        }
63        ...
64    }
65   ```
66
67
68
69### 启动Service
70
71Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Want传递给该方法来启动Service。
72
73开发者可以通过构造包含bundleName与abilityName的Want对象来设置目标Service信息。参数的含义如下:
74
75- bundleName:表示包名称。
76- abilityName:表示待启动的Ability名称。
77
78启动本地设备Service的代码示例如下:
79
80```javascript
81import featureAbility from '@ohos.ability.featureAbility';
82let promise = featureAbility.startAbility(
83    {
84        want:
85        {
86            bundleName: "com.jstest.service",
87            abilityName: "com.jstest.service.ServiceAbility",
88        },
89    }
90);
91```
92
93执行上述代码后,Ability将通过startAbility() 方法来启动Service。
94- 如果Service尚未运行,则系统会先调用onStart()来初始化Service,再回调Service的onCommand()方法来启动Service。
95- 如果Service正在运行,则系统会直接回调Service的onCommand()方法来启动Service。
96
97
98
99### 停止Service
100
101  Service一旦创建就会一直保持在后台运行,除非必须回收内存资源,否则系统不会停止或销毁Service。开发者可以在Service中通过terminateSelf()停止本Service。
102
103
104
105### 连接本地Service
106
107如果Service需要与Page Ability或其他应用的Service Ability进行交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。
108
109在使用connectAbility()处理回调时,需要传入目标Service的Want与IAbilityConnection的实例。IAbilityConnection提供了以下方法供开发者实现:onConnect()是用来处理连接Service成功的回调,onDisconnect()是用来处理Service异常死亡的回调,onFailed()是用来处理连接Service失败的回调。
110
111创建连接本地Service回调实例的代码示例如下:
112
113```javascript
114import prompt from '@system.prompt'
115
116let mRemote;
117function onConnectCallback(element, remote){
118    console.log('onConnectLocalService onConnectDone element: ' + element);
119    console.log('onConnectLocalService onConnectDone remote: ' + remote);
120    mRemote = remote;
121    if (mRemote == null) {
122      prompt.showToast({
123        message: "onConnectLocalService not connected yet"
124      });
125      return;
126    }
127    let option = new rpc.MessageOption();
128    let data = new rpc.MessageParcel();
129    let reply = new rpc.MessageParcel();
130    data.writeInt(1);
131    data.writeInt(99);
132    mRemote.sendRequest(1, data, reply, option).then((result) => {
133        console.log('sendRequest success');
134        let msg = reply.readInt();
135        prompt.showToast({
136            message: "onConnectLocalService connect result: " + msg,
137            duration: 3000
138        });
139    }).catch((e) => {
140        console.log('sendRequest error:' + e);
141    });
142
143}
144
145function onDisconnectCallback(element){
146    console.log('ConnectAbility onDisconnect Callback')
147}
148
149function onFailedCallback(code){
150    console.log('ConnectAbility onFailed Callback')
151}
152```
153
154连接本地Service的代码示例如下:
155
156```javascript
157import featureAbility from '@ohos.ability.featureAbility';
158let connId = featureAbility.connectAbility(
159    {
160        bundleName: "com.jstest.service",
161        abilityName: "com.jstest.service.ServiceAbility",
162    },
163    {
164        onConnect: onConnectCallback,
165        onDisconnect: onDisconnectCallback,
166        onFailed: onFailedCallback,
167    },
168);
169```
170
171    同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象。OpenHarmony提供了IRemoteObject的默认实现,开发者可以通过继承rpc.RemoteObject来创建自定义的实现类。
172
173Service侧把自身的实例返回给调用侧的代码示例如下:
174
175```javascript
176import rpc from "@ohos.rpc";
177
178let mMyStub;
179export default {
180    onStart() {
181        class MyStub extends rpc.RemoteObject{
182            constructor(des) {
183                if (typeof des === 'string') {
184                    super(des);
185                }
186                return null;
187            }
188            onRemoteRequest(code, data, reply, option) {
189                console.log("ServiceAbility onRemoteRequest called");
190                if (code === 1) {
191                    let op1 = data.readInt();
192                    let op2 = data.readInt();
193                    console.log("op1 = " + op1 + ", op2 = " + op2);
194                    reply.writeInt(op1 + op2);
195                } else {
196                    console.log("ServiceAbility unknown request code");
197                }
198                return true;
199            }
200        }
201        mMyStub = new MyStub("ServiceAbility-test");
202    },
203    onCommand(want, startId) {
204        console.log('ServiceAbility onCommand');
205    },
206    onConnect(want) {
207        console.log('ServiceAbility OnConnect');
208        return mMyStub;
209    },
210    onDisconnect(want) {
211        console.log('ServiceAbility OnDisConnect');
212    },
213    onStop() {
214        console.log('ServiceAbility onStop');
215    },
216}
217```
218
219### 连接远程Service<a name="section126857614019"></a>(当前仅对系统应用开放)
220
221>说明:由于DeviceManager的getTrustedDeviceListSync接口仅对系统应用开放,当前连接远程Service仅支持系统应用。
222
223如果Service需要与Page Ability或其他应用的Service Ability进行跨设备交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行跨设备连接。
224
225在使用connectAbility()处理回调时,需要传入目标Service的Want与IAbilityConnection的实例。IAbilityConnection提供了以下方法供开发者实现:onConnect()是用来处理连接Service成功的回调,onDisconnect()是用来处理Service异常死亡的回调,onFailed()是用来处理连接Service失败的回调。
226
227创建连接远程Service回调实例的代码示例如下:
228
229```ts
230import prompt from '@system.prompt'
231
232let mRemote;
233function onConnectCallback(element, remote){
234    console.log('onConnectRemoteService onConnectDone element: ' + element);
235    console.log('onConnectRemoteService onConnectDone remote: ' + remote);
236    mRemote = remote;
237    if (mRemote == null) {
238      prompt.showToast({
239        message: "onConnectRemoteService not connected yet"
240      });
241      return;
242    }
243    let option = new rpc.MessageOption();
244    let data = new rpc.MessageParcel();
245    let reply = new rpc.MessageParcel();
246    data.writeInt(1);
247    data.writeInt(99);
248    mRemote.sendRequest(1, data, reply, option).then((result) => {
249        console.log('sendRequest success');
250        let msg = reply.readInt();
251        prompt.showToast({
252            message: "onConnectRemoteService connect result: " + msg,
253            duration: 3000
254        });
255    }).catch((e) => {
256        console.log('sendRequest error:' + e);
257    });
258}
259
260function onDisconnectCallback(element){
261    console.log('ConnectRemoteAbility onDisconnect Callback')
262}
263
264function onFailedCallback(code){
265    console.log('ConnectRemoteAbility onFailed Callback')
266}
267```
268
269目标Service的Want需要包含远程deviceId,该远程deviceId可通过deviceManager获取,具体示例代码如下:
270
271```ts
272import deviceManager from '@ohos.distributedHardware.deviceManager';
273
274//dmClass具体实现请参考:相关实例 分布式Demo 章节中的实现
275let dmClass;
276
277function getRemoteDeviceId() {
278    if (typeof dmClass === 'object' && dmClass != null) {
279        let list = dmClass.getTrustedDeviceListSync();
280        if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
281            console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null");
282            return;
283        }
284        console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
285        return list[0].deviceId;
286    } else {
287        console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null");
288    }
289}
290```
291
292连接远程Service的代码示例如下:
293
294```ts
295import featureAbility from '@ohos.ability.featureAbility';
296let connId = featureAbility.connectAbility(
297    {
298        deviceId: getRemoteDeviceId(),
299        bundleName: "ohos.samples.etsDemo",
300        abilityName: "ohos.samples.etsDemo.ServiceAbility",
301    },
302    {
303        onConnect: onConnectCallback,
304        onDisconnect: onDisconnectCallback,
305        onFailed: onFailedCallback,
306    },
307);
308```
309在跨设备场景下,需要向用户申请数据同步的权限。具体示例代码如下:
310
311```ts
312import abilityAccessCtrl from "@ohos.abilityAccessCtrl";
313import bundle from '@ohos.bundle';
314async function RequestPermission() {
315  console.info('RequestPermission begin');
316  let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"];
317  let bundleFlag = 0;
318  let tokenID = undefined;
319  let userID = 100;
320  let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID);
321  tokenID = appInfo.accessTokenId;
322  let atManager = abilityAccessCtrl.createAtManager();
323  let requestPermissions: Array<string> = [];
324  for (let i = 0;i < array.length; i++) {
325    let result = await atManager.verifyAccessToken(tokenID, array[i]);
326    console.info("verifyAccessToken result:" + JSON.stringify(result));
327    if (result == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
328    } else {
329      requestPermissions.push(array[i]);
330    }
331  }
332  console.info("requestPermissions:" + JSON.stringify(requestPermissions));
333  if (requestPermissions.length == 0 || requestPermissions == []) {
334    return;
335  }
336  let context = featureAbility.getContext();
337  context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{
338    console.info("data:" + JSON.stringify(data));
339  });
340  console.info('RequestPermission end');
341}
342```
343
344同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象。OpenHarmony提供了IRemoteObject的默认实现,开发者可以通过继承rpc.RemoteObject来创建自定义的实现类。
345
346Service侧把自身的实例返回给调用侧的代码示例如下:
347
348```ts
349import rpc from "@ohos.rpc";
350
351class FirstServiceAbilityStub extends rpc.RemoteObject{
352    constructor(des) {
353        if (typeof des === 'string') {
354            super(des);
355        } else {
356            return null;
357        }
358    }
359    onRemoteRequest(code, data, reply, option) {
360        console.log("ServiceAbility onRemoteRequest called");
361        if (code === 1) {
362            let op1 = data.readInt();
363            let op2 = data.readInt();
364            console.log("op1 = " + op1 + ", op2 = " + op2);
365            reply.writeInt(op1 + op2);
366        } else {
367            console.log("ServiceAbility unknown request code");
368        }
369        return true;
370    }
371}
372
373export default {
374    onStart() {
375        console.info('ServiceAbility onStart');
376    },
377    onStop() {
378        console.info('ServiceAbility onStop');
379    },
380    onConnect(want) {
381        console.log("ServiceAbility onConnect");
382        try {
383            let value = JSON.stringify(want);
384            console.log("ServiceAbility want:" + value);
385        } catch(error) {
386            console.log("ServiceAbility error:" + error);
387        }
388        return new FirstServiceAbilityStub("first ts service stub");
389    },
390    onDisconnect(want) {
391        console.log("ServiceAbility onDisconnect");
392        let value = JSON.stringify(want);
393        console.log("ServiceAbility want:" + value);
394    },
395    onCommand(want, startId) {
396        console.info('ServiceAbility onCommand');
397        let value = JSON.stringify(want);
398        console.log("ServiceAbility want:" + value);
399        console.log("ServiceAbility startId:" + startId);
400    }
401};
402```
403
404## 相关实例
405
406针对ServiceAbility开发,有以下相关实例可供参考:
407- [`ServiceAbility`:ServiceAbility的创建与使用(eTS)(API8)](https://gitee.com/openharmony/applications_app_samples/tree/samples_monthly_0730/ability/ServiceAbility)
408- [`DMS`:分布式Demo(eTS)(API8)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/samples_monthly_0730/ability/DMS)
409