1# 长时任务 2 3## 场景说明 4 5如果应用需要在后台长时间执行用户可感知的任务,如后台播放音乐、导航、设备连接、VoIP等,则使用长时任务避免进入挂起(Suspend)状态。 6长时任务在后台执行没有时间限制。为了避免该机制被滥用,系统只允许申请有限个数的长时任务类型,同时会有相应的通知提示与长时任务相关联,使用户可感知,并且系统会添加相应的校验机制,确保应用是的确在执行相应的长时任务。 7 8## 接口说明 9 10**表1** 长时任务主要接口 11 12| 接口名 | 描述 | 13| ---------------------------------------- | ---------------------------- | 14| startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent): Promise<void> | 服务启动后,向系统申请长时任务,使服务一直保持后台运行。 | 15| stopBackgroundRunning(context: Context): Promise<void> | 停止后台长时任务的运行。 | 16 17 18其中,wantAgent的信息详见([WantAgent](../reference/apis/js-apis-wantAgent.md)) 19 20**表2** 后台模式类型 21 22| 参数名 | 描述 | 配置项 | 23| ----------------------- | -------------- | --------------------- | 24| DATA_TRANSFER | 数据传输 | dataTransfer | 25| AUDIO_PLAYBACK | 音频播放 | audioPlayback | 26| AUDIO_RECORDING | 录音 | audioRecording | 27| LOCATION | 定位导航 | location | 28| BLUETOOTH_INTERACTION | 蓝牙相关 | bluetoothInteraction | 29| MULTI_DEVICE_CONNECTION | 多设备互联 | multiDeviceConnection | 30| WIFI_INTERACTION | WLAN相关(系统保留) | wifiInteraction | 31| VOIP | 音视频通话(系统保留) | voip | 32| TASK_KEEPING | 计算任务(仅供特定设备使用) | taskKeeping | 33 34 35## 开发步骤 36 37### 基于FA模型 38 39基于FA的Service Ability使用,参考[ServiceAbility开发指导](../application-models/serviceability-overview.md)。 40 41当不需要与后台执行的长时任务交互时,可以采用startAbility()方法启动Service Ability。并在Service Ability的onStart回调方法中,调用长时任务的申请接口,声明此服务需要在后台长时运行。当任务执行完,再调用长时任务取消接口,及时释放资源。 42 43当需要与后台执行的长时任务交互时(如播放音乐等)。可以采用connectAbility()方法启动并连接Service Ability。在获取到服务的代理对象后,与服务进行通信,控制长时任务的申请和取消。 44 451、在config.json文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING、同时为需要使用长时任务的Service Ability声明相应的后台模式类型。 46 47``` 48"module": { 49 "package": "com.example.myapplication", 50 "abilities": [ 51 { 52 "backgroundModes": [ 53 "dataTransfer", 54 "location" 55 ], // 后台模式类型 56 "type": "service" // ability类型为service 57 } 58 ], 59 "reqPermissions": [ 60 { 61 "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" // 长时任务权限 62 } 63 ] 64} 65``` 66 672、在Service Ability调用长时任务的申请和取消接口。 68 69```js 70import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'; 71import featureAbility from '@ohos.ability.featureAbility'; 72import wantAgent from '@ohos.wantAgent'; 73import rpc from "@ohos.rpc"; 74 75function startContinuousTask() { 76 let wantAgentInfo = { 77 // 点击通知后,将要执行的动作列表 78 wants: [ 79 { 80 bundleName: "com.example.myapplication", 81 abilityName: "com.example.myapplication.MainAbility" 82 } 83 ], 84 // 点击通知后,动作类型 85 operationType: wantAgent.OperationType.START_ABILITY, 86 // 使用者自定义的一个私有值 87 requestCode: 0, 88 // 点击通知后,动作执行属性 89 wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 90 }; 91 92 // 通过wantAgent模块的getWantAgent方法获取WantAgent对象 93 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => { 94 try { 95 backgroundTaskManager.startBackgroundRunning(featureAbility.getContext(), 96 backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => { 97 console.info("Operation startBackgroundRunning succeeded"); 98 }).catch((err) => { 99 console.error("Operation startBackgroundRunning failed Cause: " + err); 100 }); 101 } catch (error) { 102 console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`); 103 } 104 }); 105} 106 107function stopContinuousTask() { 108 try { 109 backgroundTaskManager.stopBackgroundRunning(featureAbility.getContext()).then(() => { 110 console.info("Operation stopBackgroundRunning succeeded"); 111 }).catch((err) => { 112 console.error("Operation stopBackgroundRunning failed Cause: " + err); 113 }); 114 } catch (error) { 115 console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`); 116 } 117} 118 119async function processAsyncJobs() { 120 // 此处执行具体的长时任务。 121 122 // 长时任务执行完,调用取消接口,释放资源。 123 stopContinuousTask(); 124} 125 126let mMyStub; 127 128class MyStub extends rpc.RemoteObject { 129 constructor(des) { 130 if (typeof des === 'string') { 131 super(des); 132 } else { 133 return null; 134 } 135 } 136 onRemoteRequest(code, data, reply, option) { 137 console.log('ServiceAbility onRemoteRequest called'); 138 // code 的具体含义用户自定义 139 if (code === 1) { 140 // 接收到申请长时任务的请求码 141 startContinuousTask(); 142 // 此处执行具体长时任务 143 } else if (code === 2) { 144 // 接收到取消长时任务的请求码 145 stopContinuousTask(); 146 } else { 147 console.log('ServiceAbility unknown request code'); 148 } 149 return true; 150 } 151} 152 153export default { 154 onStart(want) { 155 console.info('ServiceAbility onStart'); 156 mMyStub = new MyStub("ServiceAbility-test"); 157 // 在执行后台长时任前,调用申请接口。 158 startContinuousTask(); 159 processAsyncJobs(); 160 }, 161 onStop() { 162 console.info('ServiceAbility onStop'); 163 }, 164 onConnect(want) { 165 console.info('ServiceAbility onConnect'); 166 return mMyStub; 167 }, 168 onReconnect(want) { 169 console.info('ServiceAbility onReconnect'); 170 }, 171 onDisconnect() { 172 console.info('ServiceAbility onDisconnect'); 173 }, 174 onCommand(want, restart, startId) { 175 console.info('ServiceAbility onCommand'); 176 } 177}; 178``` 179 180### 基于Stage模型 181 182Stage模型的相关信息参考[Stage开发概述](../application-models/stage-model-development-overview.md)。 183 1841、在module.json5文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING、同时为需要使用长时任务的ability声明相应的后台模式类型。 185 186``` 187"module": { 188 "abilities": [ 189 { 190 "backgroundModes": [ 191 "dataTransfer", 192 "location" 193 ], // 后台模式类型 194 } 195 ], 196 "requestPermissions": [ 197 { 198 "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" // 长时任务权限 199 } 200 ] 201} 202``` 203 2042、在应用内执行长时任务时,由于元能力启动管控规则限制,不支持同应用通过startAbilityByCall的形式在后台创建并运行Ability。可以直接在page中,执行相应的代码。Stage模型的Ability使用参考[Stage模型开发指导-UIAbility组件](../application-models/uiability-overview.md)。 205 206```ts 207import wantAgent from '@ohos.wantAgent'; 208import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'; 209 210@Entry 211@Component 212struct Index { 213 @State message: string = 'test' 214 // 通过getContext方法,来获取page所在的Ability上下文。 215 private context: any = getContext(this) 216 217 startContinuousTask() { 218 let wantAgentInfo = { 219 // 点击通知后,将要执行的动作列表 220 wants: [ 221 { 222 bundleName: "com.example.myapplication", 223 abilityName: "com.example.myapplication.MainAbility", 224 } 225 ], 226 // 点击通知后,动作类型 227 operationType: wantAgent.OperationType.START_ABILITY, 228 // 使用者自定义的一个私有值 229 requestCode: 0, 230 // 点击通知后,动作执行属性 231 wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 232 }; 233 234 // 通过wantAgent模块的getWantAgent方法获取WantAgent对象 235 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => { 236 try { 237 backgroundTaskManager.startBackgroundRunning(this.context, 238 backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => { 239 console.info("Operation startBackgroundRunning succeeded"); 240 }).catch((err) => { 241 console.error("Operation startBackgroundRunning failed Cause: " + err); 242 }); 243 } catch (error) { 244 console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`); 245 } 246 }); 247 } 248 249 stopContinuousTask() { 250 try { 251 backgroundTaskManager.stopBackgroundRunning(this.context).then(() => { 252 console.info("Operation stopBackgroundRunning succeeded"); 253 }).catch((err) => { 254 console.error("Operation stopBackgroundRunning failed Cause: " + err); 255 }); 256 } catch (error) { 257 console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`); 258 } 259 } 260 261 build() { 262 Row() { 263 Column() { 264 Text("Index") 265 .fontSize(50) 266 .fontWeight(FontWeight.Bold) 267 268 Button() { Text('申请长时任务').fontSize(25).fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule) 269 .margin({ top: 10 }).backgroundColor('#0D9FFB').width(250).height(40) 270 .onClick(() => { 271 // 通过按钮申请长时任务 272 this.startContinuousTask(); 273 274 // 此处执行具体的长时任务逻辑,如放音等。 275 }) 276 277 Button() { Text('取消长时任务').fontSize(25).fontWeight(FontWeight.Bold) }.type(ButtonType.Capsule) 278 .margin({ top: 10 }).backgroundColor('#0D9FFB').width(250).height(40) 279 .onClick(() => { 280 // 此处结束具体的长时任务的执行 281 282 // 通过按钮取消长时任务 283 this.stopContinuousTask(); 284 }) 285 } 286 .width('100%') 287 } 288 .height('100%') 289 } 290} 291``` 292 2933、当需要跨设备或者跨应用在后台执行长时任务时,可以通过Call的方式在后台创建并运行Ability。使用方式参考[Call调用开发指南(同设备)](../application-models/uiability-intra-device-interaction.md#通过call调用实现uiability交互仅对系统应用开放),[Call调用开发指南(跨设备)](../application-models/hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)。 294 295```ts 296import UIAbility from '@ohos.app.ability.UIAbility'; 297import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'; 298import wantAgent from '@ohos.wantAgent'; 299 300const MSG_SEND_METHOD: string = 'CallSendMsg' 301 302let mContext = null; 303 304function startContinuousTask() { 305 let wantAgentInfo = { 306 // 点击通知后,将要执行的动作列表 307 wants: [ 308 { 309 bundleName: "com.example.myapplication", 310 abilityName: "com.example.myapplication.MainAbility", 311 } 312 ], 313 // 点击通知后,动作类型 314 operationType: wantAgent.OperationType.START_ABILITY, 315 // 使用者自定义的一个私有值 316 requestCode: 0, 317 // 点击通知后,动作执行属性 318 wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 319 }; 320 321 // 通过wantAgent模块的getWantAgent方法获取WantAgent对象 322 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => { 323 try { 324 backgroundTaskManager.startBackgroundRunning(mContext, 325 backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => { 326 console.info("Operation startBackgroundRunning succeeded"); 327 }).catch((error) => { 328 console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`); 329 }); 330 } catch (error) { 331 console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`); 332 } 333 }); 334} 335 336function stopContinuousTask() { 337 try { 338 backgroundTaskManager.stopBackgroundRunning(mContext).then(() => { 339 console.info("Operation stopBackgroundRunning succeeded"); 340 }).catch((error) => { 341 console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`); 342 }); 343 } catch (error) { 344 console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`); 345 } 346} 347 348class MyParcelable { 349 num: number = 0; 350 str: String = ""; 351 352 constructor(num, string) { 353 this.num = num; 354 this.str = string; 355 } 356 357 marshalling(messageSequence) { 358 messageSequence.writeInt(this.num); 359 messageSequence.writeString(this.str); 360 return true; 361 } 362 363 unmarshalling(messageSequence) { 364 this.num = messageSequence.readInt(); 365 this.str = messageSequence.readString(); 366 return true; 367 } 368} 369 370function sendMsgCallback(data) { 371 console.info('BgTaskAbility funcCallBack is called ' + data) 372 let receivedData = new MyParcelable(0, "") 373 data.readParcelable(receivedData) 374 console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`) 375 // 可以根据Caller端发送的序列化数据的str值,执行不同的方法。 376 if (receivedData.str === 'start_bgtask') { 377 startContinuousTask() 378 } else if (receivedData.str === 'stop_bgtask') { 379 stopContinuousTask(); 380 } 381 return new MyParcelable(10, "Callee test"); 382} 383 384export default class BgTaskAbility extends Ability { 385 onCreate(want, launchParam) { 386 console.info("[Demo] BgTaskAbility onCreate") 387 this.callee.on("test", sendMsgCallback); 388 389 try { 390 this.callee.on(MSG_SEND_METHOD, sendMsgCallback) 391 } catch (error) { 392 console.error(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`) 393 } 394 mContext = this.context; 395 } 396 397 onDestroy() { 398 console.info("[Demo] BgTaskAbility onDestroy") 399 } 400 401 onWindowStageCreate(windowStage) { 402 console.info("[Demo] BgTaskAbility onWindowStageCreate") 403 404 windowStage.loadContent("pages/index").then((data)=> { 405 console.info(`load content succeed with data ${JSON.stringify(data)}`) 406 }).catch((error)=>{ 407 console.error(`load content failed with error ${JSON.stringify(error)}`) 408 }) 409 } 410 411 onWindowStageDestroy() { 412 console.info("[Demo] BgTaskAbility onWindowStageDestroy") 413 } 414 415 onForeground() { 416 console.info("[Demo] BgTaskAbility onForeground") 417 } 418 419 onBackground() { 420 console.info("[Demo] BgTaskAbility onBackground") 421 } 422}; 423``` 424