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

3
## When to Use
4 5
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 remain running in the background even after the user switches to another application.

6
## Available APIs
7

8 9 10
**Table 1** Service ability lifecycle callbacks
|API|Description|
|:------|:------|
W
wusongqing 已提交
11 12
|onStart|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.|
|onCommand|Called every time a Service ability is created on a client. You can collect calling statistics and perform initialization operations in this callback.|
13 14 15
|onConnect|Called when another ability is connected to the Service ability.|
|onDisconnect|Called when another ability is disconnected from the Service ability.|
|onStop|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

W
wusongqing 已提交
21
1. 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 24
   ```javascript
   export default {
25
       onStart() {
26 27
           console.log('ServiceAbility onStart');
       },
28
       onCommand(want, startId) {
29 30 31 32
           console.log('ServiceAbility onCommand');
       },
       onConnect(want) {
           console.log('ServiceAbility OnConnect');
W
wusongqing 已提交
33
           return null;
34
       },
35
       onDisconnect(want) {
36 37 38 39 40 41 42
           console.log('ServiceAbility OnDisConnect');
       },
       onStop() {
           console.log('ServiceAbility onStop');
       },
   }
   ```
W
wusongqing 已提交
43
   
44
2. Register a Service ability.
45

46 47 48
   You must declare your Service ability in the **config.json** file by setting its **type** attribute to **service**.
   
   ```javascript
49 50 51 52 53 54 55 56 57 58 59 60 61 62
    {
        "module": {
            "abilities": [         
                {    
                    "name": ".ServiceAbility",
                    "type": "service",
                    "visible": true
                    ...
                }
            ]
            ...
        }
        ...
    }
63
   ```
64 65 66



W
wusongqing 已提交
67
### Starting a Service Ability
68

69
The **Ability** class provides the **startAbility()** API for you to start another Service ability by passing a **Want** object.
70 71 72 73 74 75 76 77 78

To 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:

- **bundleName** indicates the name of the bundle to which the target ability belongs.
- **abilityName** indicates the target ability name.

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

```javascript
79
import featureAbility from '@ohos.ability.featureAbility';
W
wusongqing 已提交
80
let promise = featureAbility.startAbility(
81 82 83
    {
        want:
        {
W
wusongqing 已提交
84 85
            bundleName: "com.jstest.service",
            abilityName: "com.jstest.service.ServiceAbility",
86 87 88 89 90
        },
    }
); 
```

91 92 93
After the preceding code is executed, the **startAbility()** API is called to start the Service ability.
- If the Service ability is not running, the system calls **onStart()** to initialize the Service ability, and then calls **onCommand()** on the Service ability.
- If the Service ability is running, the system directly calls **onCommand()** on the Service ability.
94 95


96

W
wusongqing 已提交
97
### Stopping a Service Ability
98

W
wusongqing 已提交
99
Once 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 105 106

If 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()**.

107
When 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 109 110 111

The following code snippet shows how to implement the callbacks:

```javascript
W
wusongqing 已提交
112 113
import prompt from '@system.prompt'

114
let mRemote;
115
function onConnectCallback(element, remote){
116 117
    console.log('onConnectLocalService onConnectDone element: ' + element);
    console.log('onConnectLocalService onConnectDone remote: ' + remote);
118
    mRemote = remote;
119 120 121 122 123 124 125 126 127 128 129
    if (mRemote == null) {
      prompt.showToast({
        message: "onConnectLocalService not connected yet"
      });
      return;
    }
    let option = new rpc.MessageOption();
    let data = new rpc.MessageParcel();
    let reply = new rpc.MessageParcel();
    data.writeInt(1);
    data.writeInt(99);
130 131 132 133 134 135 136 137 138
    mRemote.sendRequest(1, data, reply, option).then((result) => {
        console.log('sendRequest success');
        let msg = reply.readInt();
        prompt.showToast({
            message: "onConnectLocalService connect result: " + msg,
            duration: 3000
        });
    }).catch((e) => {
        console.log('sendRequest error:' + e);
139
    });
140

141 142 143 144 145 146 147 148 149 150 151 152 153 154
}

function onDisconnectCallback(element){
    console.log('ConnectAbility onDisconnect Callback')
}

function onFailedCallback(code){
    console.log('ConnectAbility onFailed Callback')
}
```

The following code snippet shows how to connect to a local Service ability:

```javascript
155
import featureAbility from '@ohos.ability.featureAbility';
156
let connId = featureAbility.connectAbility(
157
    {
W
wusongqing 已提交
158 159
        bundleName: "com.jstest.service",
        abilityName: "com.jstest.service.ServiceAbility",
160 161 162 163 164 165 166 167 168
    },
    {
        onConnect: onConnectCallback,
        onDisconnect: onDisconnectCallback,
        onFailed: onFailedCallback,
    },
);
```

W
wusongqing 已提交
169
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 a default implementation of the **IRemoteObject** interface. You can extend **rpc.RemoteObject** to implement your own class of **IRemoteObject**.
170 171 172 173 174 175

The following code snippet shows how the Service ability instance returns itself to the calling ability:

```javascript
import rpc from "@ohos.rpc";

176
let mMyStub;
177
export default {
178
    onStart() {
179 180 181 182 183 184 185
        class MyStub extends rpc.RemoteObject{
            constructor(des) {
                if (typeof des === 'string') {
                    super(des);
                }
                return null;
            }
186
            onRemoteRequest(code, data, reply, option) {
187 188 189 190 191 192 193 194 195 196
                console.log("ServiceAbility onRemoteRequest called");
                if (code === 1) {
                    let op1 = data.readInt();
                    let op2 = data.readInt();
                    console.log("op1 = " + op1 + ", op2 = " + op2);
                    reply.writeInt(op1 + op2);
                } else {
                    console.log("ServiceAbility unknown request code");
                }
                return true;
197 198 199 200
            }
        }
        mMyStub = new MyStub("ServiceAbility-test");
    },
201
    onCommand(want, startId) {
202 203 204 205 206 207
        console.log('ServiceAbility onCommand');
    },
    onConnect(want) {
        console.log('ServiceAbility OnConnect');
        return mMyStub;
    },
208
    onDisconnect(want) {
209 210 211 212 213 214 215 216
        console.log('ServiceAbility OnDisConnect');
    },
    onStop() {
        console.log('ServiceAbility onStop');
    },
}
```

W
wusongqing 已提交
217 218 219 220
### Connecting to a Remote Service Ability (Applying only to System Applications)
>NOTE
>
>This feature applies only to system applications, since the **getTrustedDeviceListSync** API of the **DeviceManager** class is open only to system applications.
221

W
wusongqing 已提交
222
If 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

224
When 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 226 227

The following code snippet shows how to implement the callbacks:

228
```ts
W
wusongqing 已提交
229 230
import prompt from '@system.prompt'

231
let mRemote;
232
function onConnectCallback(element, remote){
233 234
    console.log('onConnectRemoteService onConnectDone element: ' + element);
    console.log('onConnectRemotelService onConnectDone remote: ' + remote);
235
    mRemote = remote;
236 237
    if (mRemote == null) {
      prompt.showToast({
238
        message: "onConnectRemoteService not connected yet"
239 240 241 242 243 244 245 246
      });
      return;
    }
    let option = new rpc.MessageOption();
    let data = new rpc.MessageParcel();
    let reply = new rpc.MessageParcel();
    data.writeInt(1);
    data.writeInt(99);
247 248 249 250
    mRemote.sendRequest(1, data, reply, option).then((result) => {
        console.log('sendRequest success');
        let msg = reply.readInt();
        prompt.showToast({
251
            message: "onConnectRemoteService connect result: " + msg,
252 253 254 255
            duration: 3000
        });
    }).catch((e) => {
        console.log('sendRequest error:' + e);
256
    });
257 258 259 260 261 262 263 264 265 266 267
}

function onDisconnectCallback(element){
    console.log('ConnectRemoteAbility onDisconnect Callback')
}

function onFailedCallback(code){
    console.log('ConnectRemoteAbility onFailed Callback')
}
```

268 269 270 271
The **Want** of the target Service ability must contain the remote **deviceId**, which can be obtained from **DeviceManager**. The sample code is as follows:

```ts
import deviceManager from '@ohos.distributedHardware.deviceManager';
W
wusongqing 已提交
272 273

// For details about the implementation of dmClass, see the implementation in Distributed Demo in Samples.
274
let dmClass;
W
wusongqing 已提交
275

276 277 278 279 280 281 282 283 284 285 286 287 288 289
function getRemoteDeviceId() {
    if (typeof dmClass === 'object' && dmClass != null) {
        let list = dmClass.getTrustedDeviceListSync();
        if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
            console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null");
            return;
        }
        console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
        return list[0].deviceId;
    } else {
        console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null");
    }
}
```
290 291 292

The following code snippet shows how to connect to a remote Service ability:

293
```ts
294
import featureAbility from '@ohos.ability.featureAbility';
295
let connId = featureAbility.connectAbility(
296
    {
297 298 299
        deviceId: getRemoteDeviceId(),
        bundleName: "ohos.samples.etsDemo",
        abilityName: "ohos.samples.etsDemo.ServiceAbility",
300 301 302 303 304 305 306 307
    },
    {
        onConnect: onConnectCallback,
        onDisconnect: onDisconnectCallback,
        onFailed: onFailedCallback,
    },
);
```
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code is as follows:

```ts
import abilityAccessCtrl from "@ohos.abilityAccessCtrl";
import bundle from '@ohos.bundle';
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) {
    } else {
      requestPermissions.push(array[i]);
    }
  }
  console.info("requestPermissions:" + JSON.stringify(requestPermissions));
  if (requestPermissions.length == 0 || requestPermissions == []) {
    return;
  }
  let context = featureAbility.getContext();
  context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{
    console.info("data:" + JSON.stringify(data));
  });
  console.info('RequestPermission end');
}
```
342

W
wusongqing 已提交
343
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 a default implementation of the **IRemoteObject** interface. You can extend **rpc.RemoteObject** to implement your own class of **IRemoteObject**.
344 345 346

The following code snippet shows how the Service ability instance returns itself to the calling ability:

347
```ts
348 349
import rpc from "@ohos.rpc";

350 351 352 353 354 355
class FirstServiceAbilityStub extends rpc.RemoteObject{
    constructor(des) {
        if (typeof des === 'string') {
            super(des);
        } else {
            return null;
356
        }
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
    }
    onRemoteRequest(code, data, reply, option) {
        console.log("ServiceAbility onRemoteRequest called");
        if (code === 1) {
            let op1 = data.readInt();
            let op2 = data.readInt();
            console.log("op1 = " + op1 + ", op2 = " + op2);
            reply.writeInt(op1 + op2);
        } else {
            console.log("ServiceAbility unknown request code");
        }
        return true;
    }
}

export default {
    onStart() {
        console.info('ServiceAbility onStart');
375
    },
376 377
    onStop() {
        console.info('ServiceAbility onStop');
378 379
    },
    onConnect(want) {
380 381 382 383 384 385 386 387
        console.log("ServiceAbility onConnect");
        try {
            let value = JSON.stringify(want);
            console.log("ServiceAbility want:" + value);
        } catch(error) {
            console.log("ServiceAbility error:" + error);
        }
        return new FirstServiceAbilityStub("first ts service stub");
388
    },
389 390 391 392
    onDisconnect(want) {
        console.log("ServiceAbility onDisconnect");
        let value = JSON.stringify(want);
        console.log("ServiceAbility want:" + value);
393
    },
394 395 396 397 398 399 400
    onCommand(want, startId) {
        console.info('ServiceAbility onCommand');
        let value = JSON.stringify(want);
        console.log("ServiceAbility want:" + value);
        console.log("ServiceAbility startId:" + startId);
    }
};
401 402
```

403
## Samples
404

405
The following samples are provided to help you better understand how to develop a Service ability:
W
wusongqing 已提交
406 407
- [`ServiceAbility`: Service Ability Creation and Use (eTS, API version 8)](https://gitee.com/openharmony/app_samples/tree/master/ability/ServiceAbility)
- [`DMS`: Distributed Demo (eTS, API version 8)](https://gitee.com/openharmony/app_samples/tree/master/ability/DMS)