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的整个生命周期只会调用一次。| 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销毁时调用。开发者应通过实现此方法来清理资源,如关闭线程、注册的侦听器等。| 16 17onCommand()与onConnect()的区别在于: 18 - onCommand()只能被startAbility或startAbilityForResult触发,客户端每次启动Service均会触发该回调 19 - onConnect()只能被connectAbility触发,客户端每次与Servcie建立新的连接时会触发该回调 20 21## 开发步骤 22 23### 创建注册Service 24 251. 重写Service的生命周期方法,来添加其他Ability请求与Service Ability交互时的处理方法。 26 27 创建Service的代码示例如下: 28 29 ```ts 30 export default { 31 onStart() { 32 console.log('ServiceAbility onStart'); 33 }, 34 onCommand(want, startId) { 35 console.log('ServiceAbility onCommand'); 36 }, 37 onConnect(want) { 38 console.log('ServiceAbility OnConnect'); 39 // ServiceAbilityStub的实现在下文给出 40 return new ServiceAbilityStub('test'); 41 }, 42 onDisconnect(want) { 43 console.log('ServiceAbility OnDisConnect'); 44 }, 45 onStop() { 46 console.log('ServiceAbility onStop'); 47 } 48 } 49 ``` 50 512. 注册Service。 52 53 Service需要在应用配置文件config.json中进行注册,注册类型type需要设置为service。 54 55 ```json 56 { 57 "module": { 58 "abilities": [ 59 { 60 "name": ".ServiceAbility", 61 "type": "service", 62 "visible": true 63 ... 64 } 65 ] 66 ... 67 } 68 ... 69 } 70 ``` 71 72 73 74### 启动Service 75 76Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Want传递给该方法来启动Service。 77 78开发者可以通过构造包含bundleName与abilityName的Want对象来设置目标Service信息。参数的含义如下: 79 80- bundleName:表示对端应用的包名称。 81- abilityName:表示待启动的Ability名称。 82 83启动本地设备Service的代码示例如下: 84 85```ts 86import featureAbility from '@ohos.ability.featureAbility' 87 88featureAbility.startAbility( 89 { 90 want: 91 { 92 bundleName: "com.jstest.service", 93 abilityName: "com.jstest.service.ServiceAbility" 94 } 95 } 96).then((err) => { 97 console.log("startService success"); 98}).catch (err => { 99 console.log("startService FAILED"); 100}); 101``` 102 103执行上述代码后,Ability将通过startAbility() 方法来启动Service。 104- 如果Service尚未运行,则系统会先初始化Service,然后回调onStart()来启动Service,再回调onCommand()方法。 105- 如果Service正在运行,则系统会直接回调Service的onCommand()方法。 106 107启动远端设备Service的代码示例如下,详见[连接远程Service](fa-serviceability.md#连接远程service当前仅对系统应用开放): 108 109```ts 110import featureAbility from '@ohos.ability.featureAbility' 111 112featureAbility.startAbility( 113 { 114 want: 115 { 116 deviceId: remoteDeviceId, // 远端设备Id 117 bundleName: "com.jstest.service", 118 abilityName: "com.jstest.service.ServiceAbility" 119 } 120 } 121).then((err) => { 122 console.log("startService success"); 123}).catch (err => { 124 console.log("startService FAILED"); 125}); 126``` 127 128 129### 停止Service 130 131 常规情况下,Service可以将自己停止,或者被系统停止,具体场景如下: 132 - Service调用particleAbility.terminateSelf()方法将自己停止。 133 - Service所在的应用进程退出,Service将随着进程被回收。 134 - 若Service仅仅是通过connectAbility()方法被访问的(从未执行过onCommand()回调),那么当最后一个连接被断开后,系统会将Service停止。 135 136### 连接本地Service 137 138如果Service需要与Page Ability或其他应用的Service Ability进行交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。 139 140 141开发者可使用如下两种方式实现连接Service。 142 1431. 使用IDL自动生成代码 144 145 使用OpenHarmony IDL(OpenHarmony Interface Definition Language)来自动生成对应客户端服务端及IRemoteObject代码,具体示例代码和说明请参考: 146 147 - [`OpenHarmony IDL`:TS开发步骤](../IDL/idl-guidelines.md#ts) 148 1492. 在对应文件编写代码 150 151 在使用connectAbility()时,需要传入目标Service的Want与ConnectOptions的实例,其中ConnectOptions封装了三个回调,分别对应不同情况,开发者需自行实现: 152 - onConnect():用来处理连接Service成功的回调。 153 - onDisconnect():用来处理Service断连或异常死亡的回调。 154 - onFailed():用来处理连接Service失败的回调。 155 156 创建连接本地Service回调实例的代码示例如下: 157 158 ```ts 159 import prompt from '@system.prompt' 160 161 var option = { 162 onConnect: function onConnectCallback(element, proxy) { 163 console.log(`onConnectLocalService onConnectDone`); 164 if (proxy === null) { 165 prompt.showToast({ 166 message: "Connect service failed" 167 }); 168 return; 169 } 170 // 得到Service的proxy对象后便可以与其进行通信 171 let data = rpc.MessageParcel.create(); 172 let reply = rpc.MessageParcel.create(); 173 let option = new rpc.MessageOption(); 174 data.writeString("InuptString"); 175 proxy.sendRequest(0, data, reply, option); 176 prompt.showToast({ 177 message: "Connect service success" 178 }); 179 }, 180 onDisconnect: function onDisconnectCallback(element) { 181 console.log(`onConnectLocalService onDisconnectDone element:${element}`); 182 prompt.showToast({ 183 message: "Disconnect service success" 184 }); 185 }, 186 onFailed: function onFailedCallback(code) { 187 console.log(`onConnectLocalService onFailed errCode:${code}`); 188 prompt.showToast({ 189 message: "Connect local service onFailed" 190 }); 191 } 192 }; 193 ``` 194 195 连接本地Service的代码示例如下: 196 197 ```ts 198 import featureAbility from '@ohos.ability.featureAbility' 199 200 let want = { 201 bundleName: "com.jstest.service", 202 abilityName: "com.jstest.service.ServiceAbility" 203 }; 204 let connectId = featureAbility.connectAbility(want, option); 205 ``` 206 207 同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象。OpenHarmony提供了IRemoteObject的默认实现,开发者可以通过继承rpc.RemoteObject来创建自定义的实现类,从而实现与Service的通信。具体使用方法可参考[ohos.rpc API文档](..\reference\apis\js-apis-rpc.md)。 208 209 Service侧把自身的实例返回给调用侧的代码示例如下: 210 211 ```ts 212 import rpc from "@ohos.rpc" 213 214 class ServiceAbilityStub extends rpc.RemoteObject { 215 constructor(des: any) { 216 if (typeof des === 'string') { 217 super(des); 218 } else { 219 console.log("Error, the input param is not string"); 220 return; 221 } 222 } 223 224 onRemoteRequest(code: number, data: any, reply: any, option: any) { 225 console.log("onRemoteRequest called"); 226 // 可根据code执行不同的业务逻辑 227 if (code === 1) { 228 // 将传入的字符串进行排序 229 let string = data.readString(); 230 console.log(`Input string = ${string}`); 231 let result = Array.from(string).sort().join(''); 232 console.log(`Output result = ${result}`); 233 reply.writeString(result); 234 } else { 235 console.log(`Unknown request code`); 236 } 237 return true; 238 } 239 } 240 241 export default { 242 onStart() { 243 console.log('ServiceAbility onStart'); 244 }, 245 onCommand(want, startId) { 246 console.log('ServiceAbility onCommand'); 247 }, 248 onConnect(want) { 249 console.log('ServiceAbility OnConnect'); 250 return new ServiceAbilityStub('ServiceAbilityRemoteObject'); 251 }, 252 onDisconnect(want) { 253 console.log('ServiceAbility OnDisConnect'); 254 }, 255 onStop() { 256 console.log('ServiceAbility onStop'); 257 } 258 } 259 ``` 260 261### 连接远程Service(当前仅对系统应用开放) 262 263连接远程Service,构造ConnectOptions的方法与连接本地Serivce相同,区别在于: 264 - 应用需要向用户申请数据同步权限 265 - 目标Service的Want需要包含对端设备的deviceId 266 267> 说明: 268> (1) 由于DeviceManager的getTrustedDeviceList等接口仅对系统应用开放,当前仅系统应用支持连接远程Service。 269> (2) API定义可见:[deviceManager模块](..\reference\apis\js-apis-device-manager.md) 270 271在跨设备场景下,需要向用户申请数据同步的权限,首先在config.json里配置权限: 272 273```json 274{ 275 ... 276 "module": { 277 ... 278 "reqPermissions": [{ 279 "name": "ohos.permission.DISTRIBUTED_DATASYNC" 280 }] 281 } 282} 283``` 284 285DISTRIBUTED_DATASYNC权限需要用户授予,在应用启动时需要向用户弹框请求授予权限,示例代码如下: 286 287```ts 288import abilityAccessCtrl from "@ohos.abilityAccessCtrl" 289import bundle from '@ohos.bundle' 290 291async function RequestPermission() { 292 console.info('RequestPermission begin'); 293 let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"]; 294 let bundleFlag = 0; 295 let tokenID = undefined; 296 let userID = 100; 297 let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID); 298 tokenID = appInfo.accessTokenId; 299 let atManager = abilityAccessCtrl.createAtManager(); 300 let requestPermissions: Array<string> = []; 301 for (let i = 0;i < array.length; i++) { 302 let result = await atManager.verifyAccessToken(tokenID, array[i]); 303 console.info("verifyAccessToken result:" + JSON.stringify(result)); 304 if (result != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 305 requestPermissions.push(array[i]); 306 } 307 } 308 console.info("requestPermissions:" + JSON.stringify(requestPermissions)); 309 if (requestPermissions.length == 0 || requestPermissions == []) { 310 return; 311 } 312 let context = featureAbility.getContext(); 313 context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{ 314 console.info("data:" + JSON.stringify(data)); 315 }); 316 console.info('RequestPermission end'); 317} 318``` 319 320获取deviceId需要导入`@ohos.distributedHardware.deviceManager`模块,其中提供了getTrustedDeviceList等接口用于获取远端设备的deviceId。 321 - 接口使用可参考[deviceManager模块](..\reference\apis\js-apis-device-manager.md) 322 323连接远程Service,只需要在want内定义deviceId即可,示例代码如下: 324 325```ts 326import featureAbility from '@ohos.ability.featureAbility' 327 328let want = { 329 deviceId: remoteDeviceId, 330 bundleName: "com.jstest.service", 331 abilityName: "com.jstest.service.ServiceAbility" 332}; 333let connectId = featureAbility.connectAbility(want, option); 334``` 335 336其余实现均与本地连接Service相同,参考[连接本地Service](fa-serviceability.md#连接本地service)的示例代码即可。 337 338