fa-serviceability.md 13.5 KB
Newer Older
1 2
# Service Ability Development

3
## When to Use
G
Gloria 已提交
4
A 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

G
Gloria 已提交
6
## Lifecycle APIs
7

W
wusongqing 已提交
8
**Table 1** Service ability lifecycle APIs
9 10
|API|Description|
|:------|:------|
G
Gloria 已提交
11 12
|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.|
|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.|
W
wusongqing 已提交
13 14 15
|onConnect?(want: Want): rpc.RemoteObject|Called when another ability is connected to the Service ability.|
|onDisconnect?(want: Want): void|Called when another ability is disconnected from the Service ability.|
|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

G
Gloria 已提交
17 18 19 20
The differences between **onCommand()** and **onConnect()** are as follows:
 - The **onCommand()** callback is triggered each time the client starts the Service ability by calling **startAbility** or **startAbilityForResult**.
 - The **onConnect()** callback is triggered each time the client establishes a new connection with the Service ability by calling **connectAbility**.

21
## How to Develop
22

W
wusongqing 已提交
23
### Creating and Registering a Service Ability
24

G
Gloria 已提交
25 26
1. Override the Service ability-related lifecycle callbacks to implement your own logic for processing interaction requests.
   
G
Gloria 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   ```ts
    export default {
        onStart() {
            console.log('ServiceAbility onStart');
        },
        onCommand(want, startId) {
            console.log('ServiceAbility onCommand');
        },
        onConnect(want) {
            console.log('ServiceAbility OnConnect');
            // Below lists the implementation of ServiceAbilityStub.
            return new ServiceAbilityStub('test');
        },
        onDisconnect(want) {
            console.log('ServiceAbility OnDisConnect');
        },
        onStop() {
            console.log('ServiceAbility onStop');
        }
    }
47
   ```
48
   
49
2. Register a Service ability.
50

W
wusongqing 已提交
51
   Declare the Service ability in the **config.json** file by setting its **type** attribute to **service**.
52
   
G
Gloria 已提交
53
   ```json
54
    {
G
Gloria 已提交
55 56 57 58 59 60
      "module": {
        "abilities": [
          {
            "name": ".ServiceAbility",
            "type": "service",
            "visible": true
61
            ...
G
Gloria 已提交
62 63
          }
        ]
64
        ...
G
Gloria 已提交
65 66
      }
      ...
67
    }
68
   ```
69 70 71



72
### Starting a Service Ability
73

74
The **Ability** class provides the **startAbility()** API for you to start another Service ability by passing a **Want** object.
75

G
Gloria 已提交
76
To set information about the target Service ability, you can first construct a **Want** object with the **bundleName** and **abilityName** parameters specified.  
77

G
Gloria 已提交
78 79
- **bundleName** specifies the bundle name of the target application.
- **abilityName** specifies the target ability name.
80 81 82

The following code snippet shows how to start a Service ability running on the local device:

G
Gloria 已提交
83 84 85 86
```ts
import featureAbility from '@ohos.ability.featureAbility'

featureAbility.startAbility(
87 88 89
    {
        want:
        {
W
wusongqing 已提交
90
            bundleName: "com.jstest.service",
G
Gloria 已提交
91 92
            abilityName: "com.jstest.service.ServiceAbility"
        }
93
    }
G
Gloria 已提交
94 95 96 97 98
).then((err) => {
    console.log("startService success");
}).catch (err => {
    console.log("startService FAILED");
});
99 100
```

G
Gloria 已提交
101 102
In the preceding code, the **startAbility()** API is used to start the Service ability.
- 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 104
- If the Service ability is running, the system directly calls **onCommand()** on the Service ability.

G
Gloria 已提交
105 106 107 108
The 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).

```ts
import featureAbility from '@ohos.ability.featureAbility'
W
wusongqing 已提交
109

G
Gloria 已提交
110
featureAbility.startAbility(
W
wusongqing 已提交
111 112 113
    {
        want:
        {
G
Gloria 已提交
114
            deviceId: remoteDeviceId,    // Remote device ID.
W
wusongqing 已提交
115
            bundleName: "com.jstest.service",
G
Gloria 已提交
116 117
            abilityName: "com.jstest.service.ServiceAbility"
        }
W
wusongqing 已提交
118
    }
G
Gloria 已提交
119 120 121 122 123
).then((err) => {
    console.log("startService success");
}).catch (err => {
    console.log("startService FAILED");
});
W
wusongqing 已提交
124
```
125 126


127
### Stopping a Service Ability
128

G
Gloria 已提交
129 130 131 132
  In normal cases, a Service ability can be stopped by itself or by the system.
   - The Service ability can call **particleAbility.terminateSelf()** to stop itself.
   - If the application process where the Service ability is located exits, the Service ability is reclaimed along with the process.
   - 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

G
Gloria 已提交
136
If 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


W
wusongqing 已提交
139
You can use either of the following methods to connect to a Service ability:
140

W
wusongqing 已提交
141 142
1. Using the IDL to automatically generate code

G
Gloria 已提交
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).
W
wusongqing 已提交
144 145 146

2. Writing code in the corresponding file

G
Gloria 已提交
147 148 149 150
    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.
     - **onConnect()**: callback used for processing when the Service ability is connected.
     - **onDisconnect()**: callback used for processing when the Service ability is disconnected.
     - **onFailed()**: callback used for processing when the connection to the Service ability fails.
W
wusongqing 已提交
151 152 153

    The following code snippet shows how to implement the callbacks:

G
Gloria 已提交
154
    ```ts
W
wusongqing 已提交
155
    import prompt from '@system.prompt'
156
    
W
wusongqing 已提交
157 158
    var option = {
        onConnect: function onConnectCallback(element, proxy) {
G
Gloria 已提交
159
            console.log(`onConnectLocalService onConnectDone`);
W
wusongqing 已提交
160 161 162
            if (proxy === null) {
                prompt.showToast({
                    message: "Connect service failed"
G
Gloria 已提交
163 164
                });
                return;
W
wusongqing 已提交
165
            }
G
Gloria 已提交
166 167 168 169 170 171
            // After obtaining the proxy of the Service ability, the calling ability can communicate with the Service ability.
            let data = rpc.MessageParcel.create();
            let reply = rpc.MessageParcel.create();
            let option = new rpc.MessageOption();
            data.writeString("InuptString");
            proxy.sendRequest(0, data, reply, option);
W
wusongqing 已提交
172
            prompt.showToast({
W
wusongqing 已提交
173
                message: "Connect service success"
G
Gloria 已提交
174
            });
W
wusongqing 已提交
175 176
        },
        onDisconnect: function onDisconnectCallback(element) {
G
Gloria 已提交
177
            console.log(`onConnectLocalService onDisconnectDone element:${element}`);
W
wusongqing 已提交
178 179
            prompt.showToast({
                message: "Disconnect service success"
G
Gloria 已提交
180
            });
W
wusongqing 已提交
181 182
        },
        onFailed: function onFailedCallback(code) {
G
Gloria 已提交
183
            console.log(`onConnectLocalService onFailed errCode:${code}`);
W
wusongqing 已提交
184 185
            prompt.showToast({
                message: "Connect local service onFailed"
G
Gloria 已提交
186
            });
W
wusongqing 已提交
187
        }
G
Gloria 已提交
188
    };
W
wusongqing 已提交
189
    ```
190

W
wusongqing 已提交
191
    The following code snippet shows how to connect to a local Service ability:
192

G
Gloria 已提交
193 194
    ```ts
    import featureAbility from '@ohos.ability.featureAbility'
195
    
G
Gloria 已提交
196 197 198 199 200
    let want = {
        bundleName: "com.jstest.service",
        abilityName: "com.jstest.service.ServiceAbility"
    };
    let connectId = featureAbility.connectAbility(want, option);
W
wusongqing 已提交
201
    ```
202

G
Gloria 已提交
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

G
Gloria 已提交
205
    The following code snippet shows how the Service ability returns itself to the calling ability:
206

G
Gloria 已提交
207 208
    ```ts
    import rpc from "@ohos.rpc"
209
    
G
Gloria 已提交
210 211 212 213 214 215 216 217 218
    class ServiceAbilityStub extends rpc.RemoteObject {
        constructor(des: any) {
            if (typeof des === 'string') {
                super(des);
            } else {
                console.log("Error, the input param is not string");
                return;
            }
        }
219
    
G
Gloria 已提交
220 221 222 223 224 225 226 227 228 229 230 231 232 233
        onRemoteRequest(code: number, data: any, reply: any, option: any) {
            console.log("onRemoteRequest called");
            // Execute the service logic.
            if (code === 1) {
                // Sort the input strings.
                let string = data.readString();
                console.log(`Input string = ${string}`);
                let result = Array.from(string).sort().join('');
                console.log(`Output result = ${result}`);
                reply.writeString(result);
            } else {
                console.log(`Unknown request code`);
            }
            return true;
W
wusongqing 已提交
234 235
        }
    }
236
    
G
Gloria 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    export default {
        onStart() {
            console.log('ServiceAbility onStart');
        },
        onCommand(want, startId) {
            console.log('ServiceAbility onCommand');
        },
        onConnect(want) {
            console.log('ServiceAbility OnConnect');
            return new ServiceAbilityStub('ServiceAbilityRemoteObject');
        },
        onDisconnect(want) {
            console.log('ServiceAbility OnDisConnect');
        },
        onStop() {
            console.log('ServiceAbility onStop');
W
wusongqing 已提交
253
        }
W
wusongqing 已提交
254 255 256
    }
    ```

W
wusongqing 已提交
257
### Connecting to a Remote Service Ability
258

G
Gloria 已提交
259 260 261
This 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:
 - The application must apply for the data synchronization permission from the user.
 - **Want** of the target Service ability must contain the remote device ID.
262

G
Gloria 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
> **NOTE**
>
> The **getTrustedDeviceList** API of **DeviceManager** is open only to system applications. Currently, only system applications can connect to a remote Service ability.
>
> For details about the API definition, see [Device Management](..\reference\apis\js-apis-device-manager.md).

The data synchronization permission is required in the cross-device scenario. Configure the permission in the **config.json** file.

```json
{
  ...
  "module": {
    ...
    "reqPermissions": [{
      "name": "ohos.permission.DISTRIBUTED_DATASYNC"
    }]
  }
280 281 282
}
```

G
Gloria 已提交
283
The **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
G
Gloria 已提交
286 287
import abilityAccessCtrl from "@ohos.abilityAccessCtrl"
import bundle from '@ohos.bundle'
W
wusongqing 已提交
288

G
Gloria 已提交
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
async function RequestPermission() {
    console.info('RequestPermission begin');
    let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"];
    let bundleFlag = 0;
    let tokenID = undefined;
    let userID = 100;
    let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID);
    tokenID = appInfo.accessTokenId;
    let atManager = abilityAccessCtrl.createAtManager();
    let requestPermissions: Array<string> = [];
    for (let i = 0;i < array.length; i++) {
        let result = await atManager.verifyAccessToken(tokenID, array[i]);
        console.info("verifyAccessToken result:" + JSON.stringify(result));
        if (result != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
            requestPermissions.push(array[i]);
304 305
        }
    }
G
Gloria 已提交
306 307 308
    console.info("requestPermissions:" + JSON.stringify(requestPermissions));
    if (requestPermissions.length == 0 || requestPermissions == []) {
        return;
309
    }
G
Gloria 已提交
310 311 312 313 314
    let context = featureAbility.getContext();
    context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{
        console.info("data:" + JSON.stringify(data));
    });
    console.info('RequestPermission end');
315 316
}
```
317

G
Gloria 已提交
318
To 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

G
Gloria 已提交
320
To connect to a remote Service ability, you only need to define **deviceId** in **Want**. The sample code is as follows:
321

322
```ts
G
Gloria 已提交
323
import featureAbility from '@ohos.ability.featureAbility'
324

G
Gloria 已提交
325 326 327 328
let want = {
    deviceId: remoteDeviceId,
    bundleName: "com.jstest.service",
    abilityName: "com.jstest.service.ServiceAbility"
329
};
G
Gloria 已提交
330
let connectId = featureAbility.connectAbility(want, option);
331
```
G
Gloria 已提交
332 333

The 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).