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