• 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 keep running in the background even after the user switches to another application.
5
6## Lifecycle APIs
7
8**Table 1** Service ability lifecycle APIs
9|API|Description|
10|:------|:------|
11|onStart?(): void|Called to initialize a Service ability when the Service ability is being created. This callback is invoked only once in the entire lifecycle of a Service ability.|
12|onCommand?(want: Want, startId: number): void|Called every time a Service ability is created on the 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
17The differences between **onCommand()** and **onConnect()** are as follows:
18 - The **onCommand()** callback is triggered each time the client starts the Service ability by calling **startAbility** or **startAbilityForResult**.
19 - The **onConnect()** callback is triggered each time the client establishes a new connection with the Service ability by calling **connectAbility**.
20
21## How to Develop
22
23### Creating and Registering a Service Ability
24
251. Override the Service ability-related lifecycle callbacks to implement your own logic for processing interaction requests.
26
27   ```ts
28    export default {
29        onStart() {
30            console.log('ServiceAbility onStart');
31        },
32        onCommand(want, startId) {
33            console.log('ServiceAbility onCommand');
34        },
35        onConnect(want) {
36            console.log('ServiceAbility OnConnect');
37            // Below lists the implementation of ServiceAbilityStub.
38            return new ServiceAbilityStub('test');
39        },
40        onDisconnect(want) {
41            console.log('ServiceAbility OnDisConnect');
42        },
43        onStop() {
44            console.log('ServiceAbility onStop');
45        }
46    }
47   ```
48
492. Register a Service ability.
50
51   Declare the Service ability in the **config.json** file by setting its **type** attribute to **service**.
52
53   ```json
54    {
55      "module": {
56        "abilities": [
57          {
58            "name": ".ServiceAbility",
59            "type": "service",
60            "visible": true
61            ...
62          }
63        ]
64        ...
65      }
66      ...
67    }
68   ```
69
70
71
72### Starting a Service Ability
73
74The **Ability** class provides the **startAbility()** API for you to start another Service ability by passing a **Want** object.
75
76To set information about the target Service ability, you can first construct a **Want** object with the **bundleName** and **abilityName** parameters specified.
77
78- **bundleName** specifies the bundle name of the target application.
79- **abilityName** specifies the target ability name.
80
81The following code snippet shows how to start a Service ability running on the local device:
82
83```ts
84import featureAbility from '@ohos.ability.featureAbility'
85
86featureAbility.startAbility(
87    {
88        want:
89        {
90            bundleName: "com.jstest.service",
91            abilityName: "com.jstest.service.ServiceAbility"
92        }
93    }
94).then((err) => {
95    console.log("startService success");
96}).catch (err => {
97    console.log("startService FAILED");
98});
99```
100
101In the preceding code, the **startAbility()** API is used to start the Service ability.
102- If the Service ability is not running, the system initializes the Service ability, and calls **onStart()** and **onCommand()** on the Service ability in sequence.
103- If the Service ability is running, the system directly calls **onCommand()** on the Service ability.
104
105The following code snippet shows how to start a Service ability running on the remote device. For details, see [Connecting to a Remote Service Ability](#connecting-to-a-remote-service-ability).
106
107```ts
108import featureAbility from '@ohos.ability.featureAbility'
109
110featureAbility.startAbility(
111    {
112        want:
113        {
114            deviceId: remoteDeviceId,    // Remote device ID.
115            bundleName: "com.jstest.service",
116            abilityName: "com.jstest.service.ServiceAbility"
117        }
118    }
119).then((err) => {
120    console.log("startService success");
121}).catch (err => {
122    console.log("startService FAILED");
123});
124```
125
126
127### Stopping a Service Ability
128
129  In normal cases, a Service ability can be stopped by itself or by the system.
130   - The Service ability can call **particleAbility.terminateSelf()** to stop itself.
131   - If the application process where the Service ability is located exits, the Service ability is reclaimed along with the process.
132   - If the Service ability is only accessed through **connectAbility()** (the **onCommand()** callback has never been triggered), the system stops the Service ability when the last connection to the Service ability is disconnected.
133
134### Connecting to a Local Service Ability
135
136If a Service ability wants to interact with a Page ability or a Service ability in another application, you must first create a connection. A Service ability allows other abilities to connect to it through **connectAbility()**.
137
138
139You can use either of the following methods to connect to a Service ability:
140
1411. Using the IDL to automatically generate code
142
143    Use OpenHarmony Interface Definition Language (IDL) to automatically generate the corresponding client, server, and **IRemoteObject** code. For details, see [Development Using TS](../IDL/idl-guidelines.md#development-using-ts).
144
1452. Writing code in the corresponding file
146
147    When using **connectAbility()**, pass the **Want** and **ConnectOptions** objects of the target Service ability, where **ConnectOptions** encapsulates the following three callbacks that need to be implemented.
148     - **onConnect()**: callback used for processing when the Service ability is connected.
149     - **onDisconnect()**: callback used for processing when the Service ability is disconnected.
150     - **onFailed()**: callback used for processing when the connection to the Service ability fails.
151
152    The following code snippet shows how to implement the callbacks:
153
154    ```ts
155    import prompt from '@system.prompt'
156
157    var option = {
158        onConnect: function onConnectCallback(element, proxy) {
159            console.log(`onConnectLocalService onConnectDone`);
160            if (proxy === null) {
161                prompt.showToast({
162                    message: "Connect service failed"
163                });
164                return;
165            }
166            // After obtaining the proxy of the Service ability, the calling ability can communicate with the Service ability.
167            let data = rpc.MessageParcel.create();
168            let reply = rpc.MessageParcel.create();
169            let option = new rpc.MessageOption();
170            data.writeString("InuptString");
171            proxy.sendRequest(0, data, reply, option);
172            prompt.showToast({
173                message: "Connect service success"
174            });
175        },
176        onDisconnect: function onDisconnectCallback(element) {
177            console.log(`onConnectLocalService onDisconnectDone element:${element}`);
178            prompt.showToast({
179                message: "Disconnect service success"
180            });
181        },
182        onFailed: function onFailedCallback(code) {
183            console.log(`onConnectLocalService onFailed errCode:${code}`);
184            prompt.showToast({
185                message: "Connect local service onFailed"
186            });
187        }
188    };
189    ```
190
191    The following code snippet shows how to connect to a local Service ability:
192
193    ```ts
194    import featureAbility from '@ohos.ability.featureAbility'
195
196    let want = {
197        bundleName: "com.jstest.service",
198        abilityName: "com.jstest.service.ServiceAbility"
199    };
200    let connectId = featureAbility.connectAbility(want, option);
201    ```
202
203    When 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 the default implementation of **IRemoteObject**. You can inherit **rpc.RemoteObject** to create a custom implementation class for interaction with the Service ability. For details, see the [RPC API Reference](..\reference\apis\js-apis-rpc.md).
204
205    The following code snippet shows how the Service ability returns itself to the calling ability:
206
207    ```ts
208    import rpc from "@ohos.rpc"
209
210    class ServiceAbilityStub extends rpc.RemoteObject {
211        constructor(des: any) {
212            if (typeof des === 'string') {
213                super(des);
214            } else {
215                console.log("Error, the input param is not string");
216                return;
217            }
218        }
219
220        onRemoteRequest(code: number, data: any, reply: any, option: any) {
221            console.log("onRemoteRequest called");
222            // Execute the service logic.
223            if (code === 1) {
224                // Sort the input strings.
225                let string = data.readString();
226                console.log(`Input string = ${string}`);
227                let result = Array.from(string).sort().join('');
228                console.log(`Output result = ${result}`);
229                reply.writeString(result);
230            } else {
231                console.log(`Unknown request code`);
232            }
233            return true;
234        }
235    }
236
237    export default {
238        onStart() {
239            console.log('ServiceAbility onStart');
240        },
241        onCommand(want, startId) {
242            console.log('ServiceAbility onCommand');
243        },
244        onConnect(want) {
245            console.log('ServiceAbility OnConnect');
246            return new ServiceAbilityStub('ServiceAbilityRemoteObject');
247        },
248        onDisconnect(want) {
249            console.log('ServiceAbility OnDisConnect');
250        },
251        onStop() {
252            console.log('ServiceAbility onStop');
253        }
254    }
255    ```
256
257### Connecting to a Remote Service Ability
258
259This feature applies only to system applications. The method of creating a **ConnectOptions** object for connecting to a remote Service ability is similar to that for connecting to a local Service ability. The differences are as follows:
260 - The application must apply for the data synchronization permission from the user.
261 - **Want** of the target Service ability must contain the remote device ID.
262
263> **NOTE**
264>
265> The **getTrustedDeviceList** API of **DeviceManager** is open only to system applications. Currently, only system applications can connect to a remote Service ability.
266>
267> For details about the API definition, see [Device Management](..\reference\apis\js-apis-device-manager.md).
268
269The data synchronization permission is required in the cross-device scenario. Configure the permission in the **config.json** file.
270
271```json
272{
273  ...
274  "module": {
275    ...
276    "reqPermissions": [{
277      "name": "ohos.permission.DISTRIBUTED_DATASYNC"
278    }]
279  }
280}
281```
282
283The **DISTRIBUTED_DATASYNC** permission is user granted. Therefore, your application, when being started, must display a dialog box to request the permission. The sample code is as follows:
284
285```ts
286import abilityAccessCtrl from "@ohos.abilityAccessCtrl"
287import bundle from '@ohos.bundle'
288
289async function RequestPermission() {
290    console.info('RequestPermission begin');
291    let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"];
292    let bundleFlag = 0;
293    let tokenID = undefined;
294    let userID = 100;
295    let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID);
296    tokenID = appInfo.accessTokenId;
297    let atManager = abilityAccessCtrl.createAtManager();
298    let requestPermissions: Array<string> = [];
299    for (let i = 0;i < array.length; i++) {
300        let result = await atManager.verifyAccessToken(tokenID, array[i]);
301        console.info("verifyAccessToken result:" + JSON.stringify(result));
302        if (result != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
303            requestPermissions.push(array[i]);
304        }
305    }
306    console.info("requestPermissions:" + JSON.stringify(requestPermissions));
307    if (requestPermissions.length == 0 || requestPermissions == []) {
308        return;
309    }
310    let context = featureAbility.getContext();
311    context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{
312        console.info("data:" + JSON.stringify(data));
313    });
314    console.info('RequestPermission end');
315}
316```
317
318To obtain the device ID, import the **@ohos.distributedHardware.deviceManager** module, which provides **getTrustedDeviceList** to obtain the remote device ID. For details about how to use the API, see [Device Management](..\reference\apis\js-apis-device-manager.md).
319
320To connect to a remote Service ability, you only need to define **deviceId** in **Want**. The sample code is as follows:
321
322```ts
323import featureAbility from '@ohos.ability.featureAbility'
324
325let want = {
326    deviceId: remoteDeviceId,
327    bundleName: "com.jstest.service",
328    abilityName: "com.jstest.service.ServiceAbility"
329};
330let connectId = featureAbility.connectAbility(want, option);
331```
332
333The other implementations are the same as those for the connection to a local Service ability. For details, see the sample code provided under [Connecting to a Local Service Ability](#connecting-to-a-local-service-ability).
334