1# ServiceAbility开发指导 2 3## 场景介绍 4基于Service模板的Ability(以下简称“Service”)主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动,即使用户切换到其他应用,Service仍将在后台继续运行。 5 6## 接口说明 7 8**表1** Service中相关生命周期API功能介绍 9|接口名|描述| 10|:------|:------| 11|onStart?(): void|该方法在创建Service的时候调用,用于Service的初始化。在Service的整个生命周期只会调用一次,调用时传入的Want应为空。| 12|onCommand?(want: Want, startId: number): void|在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,开发者可以在该方法中做一些调用统计、初始化类的操作。| 13|onConnect?(want: Want): rpc.RemoteObject|在Ability和Service连接时调用。| 14|onDisconnect?(want: Want): void|在Ability与绑定的Service断开连接时调用。| 15|onStop?(): void|在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。| 16 17## 开发步骤 18 19### 创建Service 20 211. 重写Service的生命周期方法,来添加其他Ability请求与Service Ability交互时的处理方法。 22 23 创建Service的代码示例如下: 24 25 ```javascript 26 export default { 27 onStart() { 28 console.log('ServiceAbility onStart'); 29 }, 30 onCommand(want, startId) { 31 console.log('ServiceAbility onCommand'); 32 }, 33 onConnect(want) { 34 console.log('ServiceAbility OnConnect'); 35 return null; 36 }, 37 onDisconnect(want) { 38 console.log('ServiceAbility OnDisConnect'); 39 }, 40 onStop() { 41 console.log('ServiceAbility onStop'); 42 }, 43 } 44 ``` 45 462. 注册Service。 47 48 Service需要在应用配置文件config.json中进行注册,注册类型type需要设置为service。 49 50 ```javascript 51 { 52 "module": { 53 "abilities": [ 54 { 55 "name": ".ServiceAbility", 56 "type": "service", 57 "visible": true 58 ... 59 } 60 ] 61 ... 62 } 63 ... 64 } 65 ``` 66 67 68 69### 启动Service 70 71Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Want传递给该方法来启动Service。 72 73开发者可以通过构造包含bundleName与abilityName的Want对象来设置目标Service信息。参数的含义如下: 74 75- bundleName:表示包名称。 76- abilityName:表示待启动的Ability名称。 77 78启动本地设备Service的代码示例如下: 79 80```javascript 81import featureAbility from '@ohos.ability.featureAbility'; 82let promise = featureAbility.startAbility( 83 { 84 want: 85 { 86 bundleName: "com.jstest.service", 87 abilityName: "com.jstest.service.ServiceAbility", 88 }, 89 } 90); 91``` 92 93执行上述代码后,Ability将通过startAbility() 方法来启动Service。 94- 如果Service尚未运行,则系统会先调用onStart()来初始化Service,再回调Service的onCommand()方法来启动Service。 95- 如果Service正在运行,则系统会直接回调Service的onCommand()方法来启动Service。 96 97 98 99### 停止Service 100 101 Service一旦创建就会一直保持在后台运行,除非必须回收内存资源,否则系统不会停止或销毁Service。开发者可以在Service中通过terminateSelf()停止本Service。 102 103 104 105### 连接本地Service 106 107如果Service需要与Page Ability或其他应用的Service Ability进行交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。 108 109在使用connectAbility()处理回调时,需要传入目标Service的Want与IAbilityConnection的实例。IAbilityConnection提供了以下方法供开发者实现:onConnect()是用来处理连接Service成功的回调,onDisconnect()是用来处理Service异常死亡的回调,onFailed()是用来处理连接Service失败的回调。 110 111创建连接本地Service回调实例的代码示例如下: 112 113```javascript 114import prompt from '@system.prompt' 115 116let mRemote; 117function onConnectCallback(element, remote){ 118 console.log('onConnectLocalService onConnectDone element: ' + element); 119 console.log('onConnectLocalService onConnectDone remote: ' + remote); 120 mRemote = remote; 121 if (mRemote == null) { 122 prompt.showToast({ 123 message: "onConnectLocalService not connected yet" 124 }); 125 return; 126 } 127 let option = new rpc.MessageOption(); 128 let data = new rpc.MessageParcel(); 129 let reply = new rpc.MessageParcel(); 130 data.writeInt(1); 131 data.writeInt(99); 132 mRemote.sendRequest(1, data, reply, option).then((result) => { 133 console.log('sendRequest success'); 134 let msg = reply.readInt(); 135 prompt.showToast({ 136 message: "onConnectLocalService connect result: " + msg, 137 duration: 3000 138 }); 139 }).catch((e) => { 140 console.log('sendRequest error:' + e); 141 }); 142 143} 144 145function onDisconnectCallback(element){ 146 console.log('ConnectAbility onDisconnect Callback') 147} 148 149function onFailedCallback(code){ 150 console.log('ConnectAbility onFailed Callback') 151} 152``` 153 154连接本地Service的代码示例如下: 155 156```javascript 157import featureAbility from '@ohos.ability.featureAbility'; 158let connId = featureAbility.connectAbility( 159 { 160 bundleName: "com.jstest.service", 161 abilityName: "com.jstest.service.ServiceAbility", 162 }, 163 { 164 onConnect: onConnectCallback, 165 onDisconnect: onDisconnectCallback, 166 onFailed: onFailedCallback, 167 }, 168); 169``` 170 171 同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象。OpenHarmony提供了IRemoteObject的默认实现,开发者可以通过继承rpc.RemoteObject来创建自定义的实现类。 172 173Service侧把自身的实例返回给调用侧的代码示例如下: 174 175```javascript 176import rpc from "@ohos.rpc"; 177 178let mMyStub; 179export default { 180 onStart() { 181 class MyStub extends rpc.RemoteObject{ 182 constructor(des) { 183 if (typeof des === 'string') { 184 super(des); 185 } 186 return null; 187 } 188 onRemoteRequest(code, data, reply, option) { 189 console.log("ServiceAbility onRemoteRequest called"); 190 if (code === 1) { 191 let op1 = data.readInt(); 192 let op2 = data.readInt(); 193 console.log("op1 = " + op1 + ", op2 = " + op2); 194 reply.writeInt(op1 + op2); 195 } else { 196 console.log("ServiceAbility unknown request code"); 197 } 198 return true; 199 } 200 } 201 mMyStub = new MyStub("ServiceAbility-test"); 202 }, 203 onCommand(want, startId) { 204 console.log('ServiceAbility onCommand'); 205 }, 206 onConnect(want) { 207 console.log('ServiceAbility OnConnect'); 208 return mMyStub; 209 }, 210 onDisconnect(want) { 211 console.log('ServiceAbility OnDisConnect'); 212 }, 213 onStop() { 214 console.log('ServiceAbility onStop'); 215 }, 216} 217``` 218 219### 连接远程Service<a name="section126857614019"></a>(当前仅对系统应用开放) 220 221>说明:由于DeviceManager的getTrustedDeviceListSync接口仅对系统应用开放,当前连接远程Service仅支持系统应用。 222 223如果Service需要与Page Ability或其他应用的Service Ability进行跨设备交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行跨设备连接。 224 225在使用connectAbility()处理回调时,需要传入目标Service的Want与IAbilityConnection的实例。IAbilityConnection提供了以下方法供开发者实现:onConnect()是用来处理连接Service成功的回调,onDisconnect()是用来处理Service异常死亡的回调,onFailed()是用来处理连接Service失败的回调。 226 227创建连接远程Service回调实例的代码示例如下: 228 229```ts 230import prompt from '@system.prompt' 231 232let mRemote; 233function onConnectCallback(element, remote){ 234 console.log('onConnectRemoteService onConnectDone element: ' + element); 235 console.log('onConnectRemoteService onConnectDone remote: ' + remote); 236 mRemote = remote; 237 if (mRemote == null) { 238 prompt.showToast({ 239 message: "onConnectRemoteService not connected yet" 240 }); 241 return; 242 } 243 let option = new rpc.MessageOption(); 244 let data = new rpc.MessageParcel(); 245 let reply = new rpc.MessageParcel(); 246 data.writeInt(1); 247 data.writeInt(99); 248 mRemote.sendRequest(1, data, reply, option).then((result) => { 249 console.log('sendRequest success'); 250 let msg = reply.readInt(); 251 prompt.showToast({ 252 message: "onConnectRemoteService connect result: " + msg, 253 duration: 3000 254 }); 255 }).catch((e) => { 256 console.log('sendRequest error:' + e); 257 }); 258} 259 260function onDisconnectCallback(element){ 261 console.log('ConnectRemoteAbility onDisconnect Callback') 262} 263 264function onFailedCallback(code){ 265 console.log('ConnectRemoteAbility onFailed Callback') 266} 267``` 268 269目标Service的Want需要包含远程deviceId,该远程deviceId可通过deviceManager获取,具体示例代码如下: 270 271```ts 272import deviceManager from '@ohos.distributedHardware.deviceManager'; 273 274//dmClass具体实现请参考:相关实例 分布式Demo 章节中的实现 275let dmClass; 276 277function getRemoteDeviceId() { 278 if (typeof dmClass === 'object' && dmClass != null) { 279 let list = dmClass.getTrustedDeviceListSync(); 280 if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { 281 console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null"); 282 return; 283 } 284 console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId); 285 return list[0].deviceId; 286 } else { 287 console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null"); 288 } 289} 290``` 291 292连接远程Service的代码示例如下: 293 294```ts 295import featureAbility from '@ohos.ability.featureAbility'; 296let connId = featureAbility.connectAbility( 297 { 298 deviceId: getRemoteDeviceId(), 299 bundleName: "ohos.samples.etsDemo", 300 abilityName: "ohos.samples.etsDemo.ServiceAbility", 301 }, 302 { 303 onConnect: onConnectCallback, 304 onDisconnect: onDisconnectCallback, 305 onFailed: onFailedCallback, 306 }, 307); 308``` 309在跨设备场景下,需要向用户申请数据同步的权限。具体示例代码如下: 310 311```ts 312import abilityAccessCtrl from "@ohos.abilityAccessCtrl"; 313import bundle from '@ohos.bundle'; 314async function RequestPermission() { 315 console.info('RequestPermission begin'); 316 let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"]; 317 let bundleFlag = 0; 318 let tokenID = undefined; 319 let userID = 100; 320 let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID); 321 tokenID = appInfo.accessTokenId; 322 let atManager = abilityAccessCtrl.createAtManager(); 323 let requestPermissions: Array<string> = []; 324 for (let i = 0;i < array.length; i++) { 325 let result = await atManager.verifyAccessToken(tokenID, array[i]); 326 console.info("verifyAccessToken result:" + JSON.stringify(result)); 327 if (result == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 328 } else { 329 requestPermissions.push(array[i]); 330 } 331 } 332 console.info("requestPermissions:" + JSON.stringify(requestPermissions)); 333 if (requestPermissions.length == 0 || requestPermissions == []) { 334 return; 335 } 336 let context = featureAbility.getContext(); 337 context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{ 338 console.info("data:" + JSON.stringify(data)); 339 }); 340 console.info('RequestPermission end'); 341} 342``` 343 344同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象。OpenHarmony提供了IRemoteObject的默认实现,开发者可以通过继承rpc.RemoteObject来创建自定义的实现类。 345 346Service侧把自身的实例返回给调用侧的代码示例如下: 347 348```ts 349import rpc from "@ohos.rpc"; 350 351class FirstServiceAbilityStub extends rpc.RemoteObject{ 352 constructor(des) { 353 if (typeof des === 'string') { 354 super(des); 355 } else { 356 return null; 357 } 358 } 359 onRemoteRequest(code, data, reply, option) { 360 console.log("ServiceAbility onRemoteRequest called"); 361 if (code === 1) { 362 let op1 = data.readInt(); 363 let op2 = data.readInt(); 364 console.log("op1 = " + op1 + ", op2 = " + op2); 365 reply.writeInt(op1 + op2); 366 } else { 367 console.log("ServiceAbility unknown request code"); 368 } 369 return true; 370 } 371} 372 373export default { 374 onStart() { 375 console.info('ServiceAbility onStart'); 376 }, 377 onStop() { 378 console.info('ServiceAbility onStop'); 379 }, 380 onConnect(want) { 381 console.log("ServiceAbility onConnect"); 382 try { 383 let value = JSON.stringify(want); 384 console.log("ServiceAbility want:" + value); 385 } catch(error) { 386 console.log("ServiceAbility error:" + error); 387 } 388 return new FirstServiceAbilityStub("first ts service stub"); 389 }, 390 onDisconnect(want) { 391 console.log("ServiceAbility onDisconnect"); 392 let value = JSON.stringify(want); 393 console.log("ServiceAbility want:" + value); 394 }, 395 onCommand(want, startId) { 396 console.info('ServiceAbility onCommand'); 397 let value = JSON.stringify(want); 398 console.log("ServiceAbility want:" + value); 399 console.log("ServiceAbility startId:" + startId); 400 } 401}; 402``` 403 404## 相关实例 405 406针对ServiceAbility开发,有以下相关实例可供参考: 407- [`ServiceAbility`:ServiceAbility的创建与使用(eTS)(API8)](https://gitee.com/openharmony/applications_app_samples/tree/samples_monthly_0730/ability/ServiceAbility) 408- [`DMS`:分布式Demo(eTS)(API8)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/samples_monthly_0730/ability/DMS) 409