• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Service Ability Development
2
3## When to Use
4A Service ability is used to run tasks in the background, such as playing music or downloading files. It does not provide a UI for user interaction. Service abilities can be started by other applications or abilities and can remain running in the background even after the user switches to another application.
5
6## Available APIs
7
8**Table 1** Service ability lifecycle APIs
9|API|Description|
10|:------|:------|
11|onStart?(): void|Called to initialize a Service ability being created. This callback is invoked only once in the entire lifecycle of a Service ability. The **Want** object passed to this callback must be null.|
12|onCommand?(want: Want, startId: number): void|Called every time a Service ability is created on a client. You can collect calling statistics and perform initialization operations in this callback.|
13|onConnect?(want: Want): rpc.RemoteObject|Called when another ability is connected to the Service ability.|
14|onDisconnect?(want: Want): void|Called when another ability is disconnected from the Service ability.|
15|onStop?(): void|Called when the Service ability is being destroyed. You should override this callback for your Service ability to clear its resources, such as threads and registered listeners.|
16
17## How to Develop
18
19### Creating a Service Ability
20
211. Create a child class of the **Ability** class and override the following Service ability-related lifecycle callbacks to implement your own logic for processing interaction requests:
22
23   ```javascript
24   export default {
25       onStart() {
26           console.log('ServiceAbility onStart');
27       },
28       onCommand(want, startId) {
29           console.log('ServiceAbility onCommand');
30       },
31       onConnect(want) {
32           console.log('ServiceAbility OnConnect');
33           return null;
34       },
35       onDisconnect(want) {
36           console.log('ServiceAbility OnDisConnect');
37       },
38       onStop() {
39           console.log('ServiceAbility onStop');
40       },
41   }
42   ```
43
442. Register a Service ability.
45
46   Declare the Service ability in the **config.json** file by setting its **type** attribute to **service**.
47
48   ```javascript
49    {
50        "module": {
51            "abilities": [
52                {
53                    "name": ".ServiceAbility",
54                    "type": "service",
55                    "visible": true
56                    ...
57                }
58            ]
59            ...
60        }
61        ...
62    }
63   ```
64
65
66
67### Starting a Service Ability
68
69The **Ability** class provides the **startAbility()** API for you to start another Service ability by passing a **Want** object.
70
71To set information about the target Service ability, you can first construct a **Want** object with the **bundleName** and **abilityName** parameters specified. The meanings of the parameters are as follows:
72
73- **bundleName** indicates the name of the bundle to which the target ability belongs.
74- **abilityName** indicates the target ability name.
75
76The following code snippet shows how to start a Service ability running on the local device:
77
78```javascript
79import featureAbility from '@ohos.ability.featureAbility';
80let promise = featureAbility.startAbility(
81    {
82        want:
83        {
84            bundleName: "com.jstest.service",
85            abilityName: "com.jstest.service.ServiceAbility",
86        },
87    }
88);
89```
90
91After the preceding code is executed, the **startAbility()** API is called to start the Service ability.
92- If the Service ability is not running, the system calls **onStart()** to initialize the Service ability, and then calls **onCommand()** on the Service ability.
93- If the Service ability is running, the system directly calls **onCommand()** on the Service ability.
94
95
96
97### Stopping a Service Ability
98
99Once created, the Service ability keeps running in the background. The system does not stop or destroy it unless memory resources must be reclaimed. You can call **terminateSelf()** on a Service ability to stop it.
100
101
102
103### Connecting to a Local Service Ability
104
105If you need to connect a Service ability to a Page ability or to a Service ability in another application, you must first implement the **IAbilityConnection** API for the connection. A Service ability allows other abilities to connect to it through **connectAbility()**.
106
107When calling **connectAbility()**, you should pass a **Want** object containing information about the target Service ability and an **IAbilityConnection** object to the API. **IAbilityConnection** provides the following callbacks that you should implement: **onConnect()**, **onDisconnect()**, and **onFailed()**. The **onConnect()** callback is invoked when a Service ability is connected, **onDisconnect()** is invoked when a Service ability is unexpectedly disconnected, and **onFailed()** is invoked when a connection to a Service ability fails.
108
109The following code snippet shows how to implement the callbacks:
110
111```javascript
112import prompt from '@system.prompt'
113
114let mRemote;
115function onConnectCallback(element, remote){
116    console.log('onConnectLocalService onConnectDone element: ' + element);
117    console.log('onConnectLocalService onConnectDone remote: ' + remote);
118    mRemote = remote;
119    if (mRemote == null) {
120      prompt.showToast({
121        message: "onConnectLocalService not connected yet"
122      });
123      return;
124    }
125    let option = new rpc.MessageOption();
126    let data = new rpc.MessageParcel();
127    let reply = new rpc.MessageParcel();
128    data.writeInt(1);
129    data.writeInt(99);
130    mRemote.sendRequest(1, data, reply, option).then((result) => {
131        console.log('sendRequest success');
132        let msg = reply.readInt();
133        prompt.showToast({
134            message: "onConnectLocalService connect result: " + msg,
135            duration: 3000
136        });
137    }).catch((e) => {
138        console.log('sendRequest error:' + e);
139    });
140
141}
142
143function onDisconnectCallback(element){
144    console.log('ConnectAbility onDisconnect Callback')
145}
146
147function onFailedCallback(code){
148    console.log('ConnectAbility onFailed Callback')
149}
150```
151
152The following code snippet shows how to connect to a local Service ability:
153
154```javascript
155import featureAbility from '@ohos.ability.featureAbility';
156let connId = featureAbility.connectAbility(
157    {
158        bundleName: "com.jstest.service",
159        abilityName: "com.jstest.service.ServiceAbility",
160    },
161    {
162        onConnect: onConnectCallback,
163        onDisconnect: onDisconnectCallback,
164        onFailed: onFailedCallback,
165    },
166);
167```
168
169When a Service ability is connected, the **onConnect()** callback is invoked and returns an **IRemoteObject** defining the proxy used for communicating with the Service ability. OpenHarmony provides a default implementation of **IRemoteObject**. You can extend **rpc.RemoteObject** to implement your own class of **IRemoteObject**.
170
171The following code snippet shows how the Service ability instance returns itself to the calling ability:
172
173```javascript
174import rpc from "@ohos.rpc";
175
176let mMyStub;
177export default {
178    onStart() {
179        class MyStub extends rpc.RemoteObject{
180            constructor(des) {
181                if (typeof des === 'string') {
182                    super(des);
183                }
184                return null;
185            }
186            onRemoteRequest(code, data, reply, option) {
187                console.log("ServiceAbility onRemoteRequest called");
188                if (code === 1) {
189                    let op1 = data.readInt();
190                    let op2 = data.readInt();
191                    console.log("op1 = " + op1 + ", op2 = " + op2);
192                    reply.writeInt(op1 + op2);
193                } else {
194                    console.log("ServiceAbility unknown request code");
195                }
196                return true;
197            }
198        }
199        mMyStub = new MyStub("ServiceAbility-test");
200    },
201    onCommand(want, startId) {
202        console.log('ServiceAbility onCommand');
203    },
204    onConnect(want) {
205        console.log('ServiceAbility OnConnect');
206        return mMyStub;
207    },
208    onDisconnect(want) {
209        console.log('ServiceAbility OnDisConnect');
210    },
211    onStop() {
212        console.log('ServiceAbility onStop');
213    },
214}
215```
216
217### Connecting to a Remote Service Ability
218>**NOTE**
219>
220>This feature applies only to system applications, since the **getTrustedDeviceListSync** API of the **DeviceManager** class is open only to system applications.
221
222If you need to connect a Service ability to a Page ability or another Service ability on a remote device, you must first implement the **IAbilityConnection** interface for the connection. A Service ability allows abilities on another device to connect to it through **connectAbility()**.
223
224When calling **connectAbility()**, you should pass a **Want** object containing information about the target Service ability and an **IAbilityConnection** object to the API. **IAbilityConnection** provides the following callbacks that you should implement: **onConnect()**, **onDisconnect()**, and **onFailed()**. The **onConnect()** callback is invoked when a Service ability is connected, **onDisconnect()** is invoked when a Service ability is unexpectedly disconnected, and **onFailed()** is invoked when a connection to a Service ability fails.
225
226The following code snippet shows how to implement the callbacks:
227
228```ts
229import prompt from '@system.prompt'
230
231let mRemote;
232function onConnectCallback(element, remote){
233    console.log('onConnectRemoteService onConnectDone element: ' + element);
234    console.log('onConnectRemoteService onConnectDone remote: ' + remote);
235    mRemote = remote;
236    if (mRemote == null) {
237      prompt.showToast({
238        message: "onConnectRemoteService not connected yet"
239      });
240      return;
241    }
242    let option = new rpc.MessageOption();
243    let data = new rpc.MessageParcel();
244    let reply = new rpc.MessageParcel();
245    data.writeInt(1);
246    data.writeInt(99);
247    mRemote.sendRequest(1, data, reply, option).then((result) => {
248        console.log('sendRequest success');
249        let msg = reply.readInt();
250        prompt.showToast({
251            message: "onConnectRemoteService connect result: " + msg,
252            duration: 3000
253        });
254    }).catch((e) => {
255        console.log('sendRequest error:' + e);
256    });
257}
258
259function onDisconnectCallback(element){
260    console.log('ConnectRemoteAbility onDisconnect Callback')
261}
262
263function onFailedCallback(code){
264    console.log('ConnectRemoteAbility onFailed Callback')
265}
266```
267
268The **Want** of the target Service ability must contain the remote **deviceId**, which can be obtained from **DeviceManager**. The sample code is as follows:
269
270```ts
271import deviceManager from '@ohos.distributedHardware.deviceManager';
272
273// For details about the implementation of dmClass, see the implementation in Distributed Demo in Samples.
274let dmClass;
275
276function getRemoteDeviceId() {
277    if (typeof dmClass === 'object' && dmClass != null) {
278        let list = dmClass.getTrustedDeviceListSync();
279        if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
280            console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null");
281            return;
282        }
283        console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
284        return list[0].deviceId;
285    } else {
286        console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null");
287    }
288}
289```
290
291The following code snippet shows how to connect to a remote Service ability:
292
293```ts
294import featureAbility from '@ohos.ability.featureAbility';
295let connId = featureAbility.connectAbility(
296    {
297        deviceId: getRemoteDeviceId(),
298        bundleName: "ohos.samples.etsDemo",
299        abilityName: "ohos.samples.etsDemo.ServiceAbility",
300    },
301    {
302        onConnect: onConnectCallback,
303        onDisconnect: onDisconnectCallback,
304        onFailed: onFailedCallback,
305    },
306);
307```
308In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code is as follows:
309
310```ts
311import abilityAccessCtrl from "@ohos.abilityAccessCtrl";
312import bundle from '@ohos.bundle';
313async function RequestPermission() {
314  console.info('RequestPermission begin');
315  let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"];
316  let bundleFlag = 0;
317  let tokenID = undefined;
318  let userID = 100;
319  let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID);
320  tokenID = appInfo.accessTokenId;
321  let atManager = abilityAccessCtrl.createAtManager();
322  let requestPermissions: Array<string> = [];
323  for (let i = 0;i < array.length; i++) {
324    let result = await atManager.verifyAccessToken(tokenID, array[i]);
325    console.info("verifyAccessToken result:" + JSON.stringify(result));
326    if (result == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
327    } else {
328      requestPermissions.push(array[i]);
329    }
330  }
331  console.info("requestPermissions:" + JSON.stringify(requestPermissions));
332  if (requestPermissions.length == 0 || requestPermissions == []) {
333    return;
334  }
335  let context = featureAbility.getContext();
336  context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{
337    console.info("data:" + JSON.stringify(data));
338  });
339  console.info('RequestPermission end');
340}
341```
342
343When a Service ability is connected, the **onConnect()** callback is invoked and returns an **IRemoteObject** defining the proxy used for communicating with the Service ability. OpenHarmony provides a default implementation of the **IRemoteObject** interface. You can extend **rpc.RemoteObject** to implement your own class of **IRemoteObject**.
344
345The following code snippet shows how the Service ability instance returns itself to the calling ability:
346
347```ts
348import rpc from "@ohos.rpc";
349
350class FirstServiceAbilityStub extends rpc.RemoteObject{
351    constructor(des) {
352        if (typeof des === 'string') {
353            super(des);
354        } else {
355            return null;
356        }
357    }
358    onRemoteRequest(code, data, reply, option) {
359        console.log("ServiceAbility onRemoteRequest called");
360        if (code === 1) {
361            let op1 = data.readInt();
362            let op2 = data.readInt();
363            console.log("op1 = " + op1 + ", op2 = " + op2);
364            reply.writeInt(op1 + op2);
365        } else {
366            console.log("ServiceAbility unknown request code");
367        }
368        return true;
369    }
370}
371
372export default {
373    onStart() {
374        console.info('ServiceAbility onStart');
375    },
376    onStop() {
377        console.info('ServiceAbility onStop');
378    },
379    onConnect(want) {
380        console.log("ServiceAbility onConnect");
381        try {
382            let value = JSON.stringify(want);
383            console.log("ServiceAbility want:" + value);
384        } catch(error) {
385            console.log("ServiceAbility error:" + error);
386        }
387        return new FirstServiceAbilityStub("first ts service stub");
388    },
389    onDisconnect(want) {
390        console.log("ServiceAbility onDisconnect");
391        let value = JSON.stringify(want);
392        console.log("ServiceAbility want:" + value);
393    },
394    onCommand(want, startId) {
395        console.info('ServiceAbility onCommand');
396        let value = JSON.stringify(want);
397        console.log("ServiceAbility want:" + value);
398        console.log("ServiceAbility startId:" + startId);
399    }
400};
401```
402