1# Continuous Task (ArkTS) 2 3## Overview 4 5### Introduction 6 7If an application has a perceivable task that needs to run in an extended period of time in the background, it can request a continuous task to prevent itself from being suspended. Examples of continuous tasks include music playback and navigation in the background. Within a continuous task, the application can concurrently request multiple types of tasks and update the task types. When the application operates in the background, the system performs consistency check to ensure that the application is executing the corresponding continuous task. Upon successful request for a continuous task, the notification panel displays the message associated with the task. If the user deletes the message, the system automatically terminates the task. 8 9### Use Cases 10 11The table below lists the types of continuous tasks, which are used in various scenarios. You can select a task type suitable for your case based on the description. 12 13**Table 1** Continuous task types 14| Name| Description| Item| Example Scenario| 15| -------- | -------- | -------- | -------- | 16| DATA_TRANSFER | Data transfer| dataTransfer | Non-hosting uploading and downloading operations, like those occurring in the background of a web browser for data transfer.| 17| AUDIO_PLAYBACK | Audio and video playback| audioPlayback | Audio and video playback in the background; audio and video casting.<br> **NOTE**<br>It can be used in atomic services.| 18| AUDIO_RECORDING | Audio recording| audioRecording | Recording and screen capture in the background.| 19| LOCATION | Positioning and navigation| location | Positioning and navigation.| 20| BLUETOOTH_INTERACTION | Bluetooth-related services| bluetoothInteraction | An application transitions into the background during the process of file transfer using Bluetooth.| 21| MULTI_DEVICE_CONNECTION | Multi-device connection| multiDeviceConnection | Distributed service connection and casting.<br> **NOTE**<br>It can be used in atomic services.| 22| <!--DelRow-->WIFI_INTERACTION | WLAN-related services (for system applications only)| wifiInteraction | An application transitions into the background during the process of file transfer using WLAN.| 23| VOIP<sup>13+</sup> | Audio and video calls| voip | Chat applications (with audio and video services) transition into the background during audio and video calls.| 24| TASK_KEEPING | Computing task (for 2-in-1 devices only).| taskKeeping | Antivirus software is running.| 25 26Description of **DATA_TRANSFER**: 27 28- During data transfer, if an application uses the [upload and download agent API](../reference/apis-basic-services-kit/js-apis-request.md) to hand over tasks to the system, the application will be suspended in the background even if it has requested the continuous task of the DATA_TRANSFER type. 29 30- During data transfer, the application needs to update the progress. If the progress is not updated for more than 10 minutes, the continuous task of the DATA_TRANSFER type will be canceled. For details about how to update the progress, see the example in [startBackgroundRunning()](../reference/apis-backgroundtasks-kit/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstartbackgroundrunning12). 31 32Description of **AUDIO_PLAYBACK**: 33 34- To implement background playback through **AUDIO_PLAYBACK**, you must use the [AVSession](../media/avsession/avsession-overview.md) for audio and video development. 35 36- Casting audio and video involves transmitting content from one device to another for playback purposes. If the application transitions to the background while casting, the continuous task checks the audio and video playback and casting services. The task will persist as long as either the audio and video playback or casting service is running properly. 37 38### Constraints 39 40**Ability restrictions**: In the stage model, only the UIAbility can request continuous tasks. In the FA model, only the ServiceAbility can request continuous tasks. Continuous tasks can be requested by the current application on the current device or across devices or by other applications. However, the capability to make cross-device or cross-application requests is restricted to system applications. 41 42**Quantity restrictions**: A UIAbility (ServiceAbility in the FA model) can request only one continuous task at a time. If a UIAbility has a running continuous task, it can request another one only after the running task is finished. If an application needs to request multiple continuous tasks at the same time, it must create multiple UIAbilities. After a UIAbility requests a continuous task, all the processes of the application are not suspended. 43 44**Running restrictions**: 45 46- If an application requests a continuous task but does not carry out the relevant service, the system imposes restrictions on the application. For example, if the system detects that an application has requested a continuous task of the AUDIO_PLAYBACK type but does not play audio, the system cancels the continuous task. 47 48- If an application requests a continuous task but carries out a service that does not match the requested type, the system imposes restrictions on the application. For example, if the system detects that an application requests a continuous task of the AUDIO_PLAYBACK type, but the application is playing audio (corresponding to the AUDIO_PLAYBACK type) and recording (corresponding to the AUDIO_RECORDING type), the system enforces management measures. 49 50- When an application's operations are completed after a continuous task request, the system imposes restrictions on the application. 51 52- If the background load of the process that runs a continuous task is higher than the corresponding typical load for a long period of time, the system performs certain control. 53 54> **NOTE** 55> 56> The application shall proactively cancel a continuous task when it is finished. Otherwise, the system will forcibly cancel the task. For example, when a user taps the UI to pause music playback, the application must cancel the continuous task in a timely manner. When the user taps the UI again to continue music playback, the application needs to request a continuous task. 57> 58> If an application that plays an audio in the background is [interrupted](../media/audio/audio-playback-concurrency.md), the system automatically detects and stops the continuous task. The application must request a continuous task again to restart the playback. 59> 60> When an application that plays audio in the background stops a continuous task, it must suspend or stop the audio stream. Otherwise, the application will be forcibly terminated by the system. 61 62## Available APIs 63 64**Table 2** Main APIs for continuous tasks 65 66The table below uses promise as an example to describe the APIs used for developing continuous tasks. For details about more APIs and their usage, see [Background Task Management](../reference/apis-backgroundtasks-kit/js-apis-resourceschedule-backgroundTaskManager.md). 67 68| API| Description| 69| -------- | -------- | 70| startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: [WantAgent](../reference/apis-ability-kit/js-apis-app-ability-wantAgent.md)): Promise<void> | Requests a continuous task.| 71| stopBackgroundRunning(context: Context): Promise<void> | Cancels a continuous task.| 72 73## How to Develop 74 75The following walks you through how to request a continuous task for recording to implement the following functions: 76 77- When a user touches **Request Continuous Task**, the application requests a continuous task for recording, and a message is displayed in the notification bar, indicating that a recording task is running. 78 79- When a user touches **Cancel Continuous Task**, the application cancels the continuous task, and the notification message is removed. 80 81### Stage Model 82 831. Declare the **ohos.permission.KEEP_BACKGROUND_RUNNING** permission. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md). 84 852. Declare the continuous task type. 86 Declare the type of the continuous task for the target UIAbility in the **module.json5** file. Set the corresponding [configuration item](continuous-task.md#use-cases) in the configuration file. 87 88 ```json 89 "module": { 90 "abilities": [ 91 { 92 "backgroundModes": [ 93 // Configuration item of the continuous task type 94 "audioRecording" 95 ] 96 } 97 ], 98 // ... 99 } 100 ``` 101 1023. Import the modules. 103 104 Import the modules related to continuous tasks: @ohos.resourceschedule.backgroundTaskManager and @ohos.app.ability.wantAgent. Import other modules based on the project requirements. 105 106 ```ts 107 import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; 108 import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 109 import { window } from '@kit.ArkUI'; 110 import { rpc } from '@kit.IPCKit' 111 import { BusinessError } from '@kit.BasicServicesKit'; 112 import { wantAgent, WantAgent } from '@kit.AbilityKit'; 113 ``` 114 1154. Request and cancel a continuous task. 116 117 The code snippet below shows how an application requests a continuous task for itself. 118 119 ```ts 120 function callback(info: backgroundTaskManager.ContinuousTaskCancelInfo) { 121 // ID of a continuous task. 122 console.info('OnContinuousTaskCancel callback id ' + info.id); 123 // Reason for canceling the continuous task. 124 console.info('OnContinuousTaskCancel callback reason ' + info.reason); 125 } 126 127 @Entry 128 @Component 129 struct Index { 130 @State message: string = 'ContinuousTask'; 131 // Use getContext to obtain the context of the UIAbility for the page. 132 private context: Context = getContext(this); 133 134 OnContinuousTaskCancel() { 135 try { 136 backgroundTaskManager.on("continuousTaskCancel", callback); 137 console.info(`Succeeded in operationing OnContinuousTaskCancel.`); 138 } catch (error) { 139 console.error(`Operation OnContinuousTaskCancel failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); 140 } 141 } 142 143 OffContinuousTaskCancel() { 144 try { 145 // If the callback parameter is not passed, all callbacks associated with the specified event are canceled. 146 backgroundTaskManager.off("continuousTaskCancel", callback); 147 console.info(`Succeeded in operationing OffContinuousTaskCancel.`); 148 } catch (error) { 149 console.error(`Operation OffContinuousTaskCancel failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); 150 } 151 } 152 153 startContinuousTask() { 154 let wantAgentInfo: wantAgent.WantAgentInfo = { 155 // List of operations to be executed after the notification is clicked. 156 // Add the bundleName and abilityName of the application to start. 157 wants: [ 158 { 159 bundleName: "com.example.myapplication", 160 abilityName: "MainAbility" 161 } 162 ], 163 // Specify the action to perform (starting the ability) after the notification message is clicked. 164 actionType: wantAgent.OperationType.START_ABILITY, 165 // Custom request code. 166 requestCode: 0, 167 // Execution attribute of the operation to perform after the notification is clicked. 168 actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG], 169 // CAR_KEY subtype, which takes effect only when a continuous task of the bluetoothInteraction type is requested. 170 // Ensure that the key value in the extraInfo parameter is backgroundTaskManager.BackgroundModeType.SUB_MODE. Otherwise, the subtype does not take effect. 171 // extraInfo: { [backgroundTaskManager.BackgroundModeType.SUB_MODE] : backgroundTaskManager.BackgroundSubMode.CAR_KEY } 172 }; 173 174 try { 175 // Obtain the WantAgent object by using the getWantAgent API of the wantAgent module. 176 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => { 177 try { 178 let list: Array<string> = ["audioRecording"]; 179 // let list: Array<string> = ["bluetoothInteraction"]; The bluetoothInteraction type is included in the continuous task, and the CAR_KEY subtype is valid. 180 backgroundTaskManager.startBackgroundRunning(this.context, list, wantAgentObj).then((res: backgroundTaskManager.ContinuousTaskNotification) => { 181 console.info("Operation startBackgroundRunning succeeded"); 182 // Execute the continuous task logic, for example, recording. 183 }).catch((error: BusinessError) => { 184 console.error(`Failed to Operation startBackgroundRunning. code is ${error.code} message is ${error.message}`); 185 }); 186 } catch (error) { 187 console.error(`Failed to Operation startBackgroundRunning. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); 188 } 189 }); 190 } catch (error) { 191 console.error(`Failed to Operation getWantAgent. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); 192 } 193 } 194 195 196 stopContinuousTask() { 197 backgroundTaskManager.stopBackgroundRunning(this.context).then(() => { 198 console.info(`Succeeded in operationing stopBackgroundRunning.`); 199 }).catch((err: BusinessError) => { 200 console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`); 201 }); 202 } 203 204 build() { 205 Row() { 206 Column() { 207 Text("Index") 208 .fontSize(50) 209 .fontWeight(FontWeight.Bold) 210 211 Button() { 212 Text('Request continuous task').fontSize(25).fontWeight(FontWeight.Bold) 213 } 214 .type(ButtonType.Capsule) 215 .margin({ top: 10 }) 216 .backgroundColor('#0D9FFB') 217 .width(250) 218 .height(40) 219 .onClick(() => { 220 // Request a continuous task by clicking a button. 221 this.startContinuousTask(); 222 }) 223 224 Button() { 225 Text('Cancel continuous task').fontSize (25).fontWeight (FontWeight.Bold) 226 } 227 .type(ButtonType.Capsule) 228 .margin({ top: 10 }) 229 .backgroundColor('#0D9FFB') 230 .width(250) 231 .height(40) 232 .onClick(() => { 233 // Stop the continuous task. 234 235 // Cancel the continuous task by clicking a button. 236 this.stopContinuousTask(); 237 }) 238 239 Button() { 240 Text('Register a callback for canceling a continuous task').fontSize (25).fontWeight(FontWeight.Bold) 241 } 242 .type(ButtonType.Capsule) 243 .margin({ top: 10 }) 244 .backgroundColor('#0D9FFB') 245 .width(250) 246 .height(40) 247 .onClick(() => { 248 // Use a button to register a callback for canceling a continuous task. 249 this.OnContinuousTaskCancel(); 250 }) 251 252 Button() { 253 Text('Unregister a callback for canceling a continuous task').fontSize (25).fontWeight(FontWeight.Bold) 254 } 255 .type(ButtonType.Capsule) 256 .margin({ top: 10 }) 257 .backgroundColor('#0D9FFB') 258 .width(250) 259 .height(40) 260 .onClick(() => { 261 // Use a button to unregister a callback for canceling a continuous task. 262 this.OffContinuousTaskCancel(); 263 }) 264 } 265 .width('100%') 266 } 267 .height('100%') 268 } 269 } 270 ``` 271 <!--Del--> 272 273 The code snippet below shows how an application requests a continuous task across devices or applications. When a continuous task is executed across devices or applications in the background, the UIAbility can be created and run in the background in call mode. For details, see [Using Call to Implement UIAbility Interaction (for System Applications Only)](../application-models/uiability-intra-device-interaction.md#using-call-to-implement-uiability-interaction-for-system-applications-only) and [Using Cross-Device Call](../application-models/hop-multi-device-collaboration.md#using-cross-device-call). 274 275 ```ts 276 const MSG_SEND_METHOD: string = 'CallSendMsg' 277 278 let mContext: Context; 279 280 function startContinuousTask() { 281 let wantAgentInfo : wantAgent.WantAgentInfo = { 282 // List of operations to be executed after the notification is clicked. 283 wants: [ 284 { 285 bundleName: "com.example.myapplication", 286 abilityName: "com.example.myapplication.MainAbility", 287 } 288 ], 289 // Type of the operation to perform after the notification is clicked. 290 actionType: wantAgent.OperationType.START_ABILITY, 291 // Custom request code. 292 requestCode: 0, 293 // Execution attribute of the operation to perform after the notification is clicked. 294 actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 295 }; 296 297 // Obtain the WantAgent object by using the getWantAgent API of the wantAgent module. 298 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj : WantAgent) => { 299 backgroundTaskManager.startBackgroundRunning(mContext, 300 backgroundTaskManager.BackgroundMode.AUDIO_RECORDING, wantAgentObj).then(() => { 301 console.info(`Succeeded in operationing startBackgroundRunning.`); 302 }).catch((err: BusinessError) => { 303 console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`); 304 }); 305 }); 306 } 307 308 function stopContinuousTask() { 309 backgroundTaskManager.stopBackgroundRunning(mContext).then(() => { 310 console.info(`Succeeded in operationing stopBackgroundRunning.`); 311 }).catch((err: BusinessError) => { 312 console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`); 313 }); 314 } 315 316 class MyParcelable implements rpc.Parcelable { 317 num: number = 0; 318 str: string = ''; 319 320 constructor(num: number, string: string) { 321 this.num = num; 322 this.str = string; 323 } 324 325 marshalling(messageSequence: rpc.MessageSequence) { 326 messageSequence.writeInt(this.num); 327 messageSequence.writeString(this.str); 328 return true; 329 } 330 331 unmarshalling(messageSequence: rpc.MessageSequence) { 332 this.num = messageSequence.readInt(); 333 this.str = messageSequence.readString(); 334 return true; 335 } 336 } 337 338 function sendMsgCallback(data: rpc.MessageSequence) { 339 console.info('BgTaskAbility funcCallBack is called ' + data); 340 let receivedData: MyParcelable = new MyParcelable(0, ''); 341 data.readParcelable(receivedData); 342 console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`); 343 // You can execute different methods based on the str value in the sequenceable data sent by the caller object. 344 if (receivedData.str === 'start_bgtask') { 345 // Request a continuous task. 346 startContinuousTask(); 347 } else if (receivedData.str === 'stop_bgtask') { 348 // Cancel the continuous task. 349 stopContinuousTask(); 350 } 351 return new MyParcelable(10, 'Callee test'); 352 } 353 354 export default class BgTaskAbility extends UIAbility { 355 // Create an ability. 356 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 357 console.info("[Demo] BgTaskAbility onCreate"); 358 try { 359 this.callee.on(MSG_SEND_METHOD, sendMsgCallback) 360 } catch (error) { 361 console.error(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`); 362 } 363 mContext = this.context; 364 } 365 366 // Destroy an ability. 367 onDestroy() { 368 console.info('[Demo] BgTaskAbility onDestroy'); 369 } 370 371 onWindowStageCreate(windowStage: window.WindowStage) { 372 console.info('[Demo] BgTaskAbility onWindowStageCreate'); 373 374 windowStage.loadContent('pages/Index', (error, data) => { 375 if (error.code) { 376 console.error(`load content failed with error ${JSON.stringify(error)}`); 377 return; 378 } 379 console.info(`load content succeed with data ${JSON.stringify(data)}`); 380 }); 381 } 382 383 onWindowStageDestroy() { 384 console.info('[Demo] BgTaskAbility onWindowStageDestroy'); 385 } 386 387 onForeground() { 388 console.info('[Demo] BgTaskAbility onForeground'); 389 } 390 391 onBackground() { 392 console.info('[Demo] BgTaskAbility onBackground'); 393 } 394 }; 395 ``` 396 397 <!--DelEnd--> 398 399<!--Del--> 400### FA Model 401 4021. Start and connect to a ServiceAbility. 403 404 - If no user interaction is required, use **startAbility()** to start the ServiceAbility. For details, see [ServiceAbility Component](../application-models/serviceability-overview.md). In the **onStart** callback of the ServiceAbility, call the APIs to request and cancel continuous tasks. 405 406 - If user interaction is required (for example, in music playback scenarios), use **connectAbility()** to start and connect to the ServiceAbility. For details, see [ServiceAbility Component](../application-models/serviceability-overview.md). After obtaining the agent of the ServiceAbility, the application can communicate with the ServiceAbility and control the request and cancellation of continuous tasks. 407 4082. Configure permissions and declare the continuous task type. 409 410 Declare the **ohos.permission.KEEP_BACKGROUND_RUNNING** permission in the **config.json** file. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md). In addition, declare the continuous task type for the ServiceAbility. 411 412 ```json 413 "module": { 414 "package": "com.example.myapplication", 415 "abilities": [ 416 { 417 "backgroundModes": [ 418 "audioRecording", 419 ], // Background mode 420 "type": "service" // The ability type is Service. 421 } 422 ], 423 "reqPermissions": [ 424 { 425 "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" // Continuous task permission 426 } 427 ] 428 } 429 ``` 430 4313. Import the modules. 432 433 ```js 434 import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; 435 import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 436 import { window } from '@kit.ArkUI'; 437 import { rpc } from '@kit.IPCKit' 438 import { BusinessError } from '@kit.BasicServicesKit'; 439 import { wantAgent, WantAgent } from '@kit.AbilityKit'; 440 ``` 441 4424. Request and cancel a continuous task. In the ServiceAbility, call [startBackgroundRunning](#available-apis) and [stopBackgroundRunning](#available-apis) to request and cancel a continuous task. Use JavaScript code to implement this scenario. 443 444 ```js 445 function startContinuousTask() { 446 let wantAgentInfo: wantAgent.WantAgentInfo = { 447 // List of operations to be executed after the notification is clicked. 448 wants: [ 449 { 450 bundleName: "com.example.myapplication", 451 abilityName: "com.example.myapplication.MainAbility" 452 } 453 ], 454 // Type of the operation to perform after the notification is clicked. 455 actionType: wantAgent.OperationType.START_ABILITY, 456 // Custom request code. 457 requestCode: 0, 458 // Execution attribute of the operation to perform after the notification is clicked. 459 actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 460 }; 461 462 // Obtain the WantAgent object by using the getWantAgent API of the wantAgent module. 463 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => { 464 backgroundTaskManager.startBackgroundRunning(featureAbility.getContext(), 465 backgroundTaskManager.BackgroundMode.AUDIO_RECORDING, wantAgentObj).then(() => { 466 console.info(`Succeeded in operationing startBackgroundRunning.`); 467 }).catch((err: BusinessError) => { 468 console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`); 469 }); 470 }); 471 } 472 473 function stopContinuousTask() { 474 backgroundTaskManager.stopBackgroundRunning(featureAbility.getContext()).then(() => { 475 console.info(`Succeeded in operationing stopBackgroundRunning.`); 476 }).catch((err: BusinessError) => { 477 console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`); 478 }); 479 } 480 481 async function processAsyncJobs() { 482 // Execute the continuous task. 483 484 // After the continuous task is complete, call the API to release resources. 485 stopContinuousTask(); 486 } 487 488 let mMyStub: MyStub; 489 490 // Start the service by calling connectAbility(). 491 class MyStub extends rpc.RemoteObject { 492 constructor(des: string) { 493 super(des); 494 } 495 496 onRemoteRequest(code: number, data: rpc.MessageParcel, reply: rpc.MessageParcel, option: rpc.MessageOption) { 497 console.log('ServiceAbility onRemoteRequest called'); 498 // Custom request code. 499 if (code === 1) { 500 // Receive the request code for requesting a continuous task. 501 startContinuousTask(); 502 // Execute the continuous task. 503 } else if (code === 2) { 504 // Receive the request code for canceling the continuous task. 505 stopContinuousTask(); 506 } else { 507 console.log('ServiceAbility unknown request code'); 508 } 509 return true; 510 } 511 } 512 513 // Start the service by calling startAbility(). 514 class ServiceAbility { 515 onStart(want: Want) { 516 console.info('ServiceAbility onStart'); 517 mMyStub = new MyStub("ServiceAbility-test"); 518 // Call the API to start the task. 519 startContinuousTask(); 520 processAsyncJobs(); 521 } 522 523 onStop() { 524 console.info('ServiceAbility onStop'); 525 } 526 527 onConnect(want: Want) { 528 console.info('ServiceAbility onConnect'); 529 return mMyStub; 530 } 531 532 onReconnect(want: Want) { 533 console.info('ServiceAbility onReconnect'); 534 } 535 536 onDisconnect() { 537 console.info('ServiceAbility onDisconnect'); 538 } 539 540 onCommand(want: Want, startId: number) { 541 console.info('ServiceAbility onCommand'); 542 } 543 } 544 545 export default new ServiceAbility(); 546 ``` 547<!--DelEnd--> 548 549