写点什么

【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务,解决屏幕录制音乐播放等操作不被挂起

作者:GeorgeGcs
  • 2025-03-24
    上海
  • 本文字数:5755 字

    阅读完需:约 19 分钟

【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务,解决屏幕录制音乐播放等操作不被挂起

【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务,解决屏幕录制音乐播放等操作不被挂起

一、前言

1.后台是什么?了解后台任务和长时任务前,我们需要先明白鸿蒙的后台特性:所谓的后台,指的是设备返回主界面、锁屏、应用切换等操作会使应用退至后台这个状态。


2.鸿蒙系统为什么这么做?当应用退至后台后,如果继续活动,可能会造成设备耗电快、用户界面卡顿等现象。鸿蒙系统为了降低设备耗电速度、保障用户使用流畅度,系统会对退至后台的应用进行管控,包括进程挂起和进程终止。


3.会有什么问题?当系统将应用挂起后,应用进程无法使用软件资源(如公共事件、定时器等)和硬件资源(CPU、网络、GPS、蓝牙等)。


综上所述,所以才会有标题存在的问题和对应的解决方案。当我们应用正在使用蓝牙扫描 或者 音乐播放 或者 屏幕录制等类似的操作时,只要应用退到了后台超过三秒,就会被系统挂起,强制暂停。影响我们的逻辑业务。所以这种情况下,鸿蒙提供了后台任务来解决。

二、后台任务是什么

后台任务是鸿蒙系统提供给有在后台,做业务操作不想被挂起需求的应用,提供的一套解决方案。


根据应用业务类型不同,也分为不同的后台任务:



根据我们的常规使用场景,例如屏幕录制举例,就需要使用长时任务来解决应用被挂起的问题。

三、长时任务的使用:

1.首先我们需要根据自己的业务类型,选择对应的长时任务类型:



我们以屏幕录制举例,选择 AUDIO_RECORDING


2.在 module.json5 配置后台任务权限和长时任务能力类型:


   "abilities": [      {        "backgroundModes": ["audioRecording"],    }
复制代码


      // 申请长时任务      {        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",        "reason": "$string:reason",        "usedScene": {          "abilities": [            "EntryAbility"          ],          "when": "always"        }      },
复制代码


3.在录屏开启前,调用开启长时任务,退出录屏后取消长时任务。【开启和取消的两个调用时机需要注意,相当于长时任务的生命周期,是包裹住整个后台业务的生命周期。】


开启长时任务


import { backgroundTaskManager } from '@kit.BackgroundTasksKit';import { wantAgent, WantAgent } from '@kit.AbilityKit';  /**   * 开启长时任务   */  startContinuousTask() {    let wantAgentInfo: wantAgent.WantAgentInfo = {      // 点击通知后,将要执行的动作列表      // 添加需要被拉起应用的bundleName和abilityName      wants: [        {          bundleName: "com.test.basedemo",          abilityName: "EntryAbility"        }      ],      // 指定点击通知栏消息后的动作是拉起ability      actionType: wantAgent.OperationType.START_ABILITY,      // 使用者自定义的一个私有值      requestCode: 0,      // 点击通知后,动作执行属性      actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]    };
try { // 通过wantAgent模块下getWantAgent方法获取WantAgent对象 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => { try { backgroundTaskManager.startBackgroundRunning(getContext(), backgroundTaskManager.BackgroundMode.AUDIO_RECORDING, wantAgentObj, (error: BusinessError)=>{ if (error) { console.error(this.TAG,`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`); } else { console.info(this.TAG,"Operation startBackgroundRunning succeeded"); promptAction.showToast({ message: "开启长时任务成功!" }); // // 此处执行具体的长时任务逻辑,如录音,录制等。 // this.startRecording(); } }) } catch (error) { console.error(this.TAG,`Operation startBackgroundRunning failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); } }); } catch (error) { console.error(this.TAG, `Failed to Operation getWantAgent. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); } }
复制代码


关闭长时任务


  /**   * 暂停长时任务   */  stopContinuousTask() {    backgroundTaskManager.stopBackgroundRunning(getContext()).then(() => {      console.info(this.TAG, `Succeeded in operationing stopBackgroundRunning.`);      promptAction.showToast({        message: "取消长时任务!"      });    }).catch((err: BusinessError) => {      console.error(this.TAG, `Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);    });  }
复制代码

源码示例:


module.json5


   "abilities": [      {        "backgroundModes": ["audioRecording"],    }
复制代码


      // 申请长时任务      {        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",        "reason": "$string:reason",        "usedScene": {          "abilities": [            "EntryAbility"          ],          "when": "always"        }      },      // 申请麦克风      {        "name": "ohos.permission.MICROPHONE",        "reason": "$string:reason",        "usedScene": {          "abilities": [            "EntryAbility"          ],          "when": "always"        }      },
复制代码


BackTaskTestPage.ets



import media from '@ohos.multimedia.media';import { backgroundTaskManager } from '@kit.BackgroundTasksKit';import { abilityAccessCtrl, common } from '@kit.AbilityKit';import { BusinessError } from '@kit.BasicServicesKit';import { wantAgent, WantAgent } from '@kit.AbilityKit';import { fileIo as fs } from '@kit.CoreFileKit';import { promptAction } from '@kit.ArkUI';
@Entry@Componentstruct BackTaskTestPage {
private TAG: string = "BackTaskTestPage";
// 录屏沙箱文件 private mFile: fs.File | null = null;
aboutToAppear(): void { // 为了录屏可以采集麦克风,需要申请麦克风权限 const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); try { atManager.requestPermissionsFromUser(getContext(), ["ohos.permission.MICROPHONE"]).then((data) => { if (data.authResults[0] === 0) {
} else { console.log(this.TAG, "user rejected") } }).catch((err: BusinessError) => { console.log(this.TAG, "BusinessError err: " + JSON.stringify(err)) }) } catch (err) { console.log(this.TAG, "catch err: " + JSON.stringify(err)) }
// 创建录制视频的沙箱文件地址 let context = getContext(this) as common.UIAbilityContext; // 获取设备A的UIAbilityContext信息 let pathDir: string = context.filesDir; // /data/storage/el2/base/haps/entry/files let filePath: string = pathDir + '/testBG.mp4'; // 若文件不存在,则创建文件。 let fileTarget = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); console.info(this.TAG, "file done: " + JSON.stringify(fileTarget.fd)); this.mFile = fileTarget;
}
/** * 开启长时任务 */ startContinuousTask() { let wantAgentInfo: wantAgent.WantAgentInfo = { // 点击通知后,将要执行的动作列表 // 添加需要被拉起应用的bundleName和abilityName wants: [ { bundleName: "com.test.basedemo", abilityName: "EntryAbility" } ], // 指定点击通知栏消息后的动作是拉起ability actionType: wantAgent.OperationType.START_ABILITY, // 使用者自定义的一个私有值 requestCode: 0, // 点击通知后,动作执行属性 actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] };
try { // 通过wantAgent模块下getWantAgent方法获取WantAgent对象 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => { try { backgroundTaskManager.startBackgroundRunning(getContext(), backgroundTaskManager.BackgroundMode.AUDIO_RECORDING, wantAgentObj, (error: BusinessError)=>{ if (error) { console.error(this.TAG,`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`); } else { console.info(this.TAG,"Operation startBackgroundRunning succeeded"); promptAction.showToast({ message: "开启长时任务成功!" }); // // 此处执行具体的长时任务逻辑,如录音,录制等。 // this.startRecording(); } }) } catch (error) { console.error(this.TAG,`Operation startBackgroundRunning failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); } }); } catch (error) { console.error(this.TAG, `Failed to Operation getWantAgent. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`); } }
/** * 暂停长时任务 */ stopContinuousTask() { backgroundTaskManager.stopBackgroundRunning(getContext()).then(() => { console.info(this.TAG, `Succeeded in operationing stopBackgroundRunning.`); promptAction.showToast({ message: "取消长时任务!" }); }).catch((err: BusinessError) => { console.error(this.TAG, `Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`); }); }
build() { Row() { Column() { Button() { Text('申请长时任务').fontSize(25).fontWeight(FontWeight.Bold) } .type(ButtonType.Capsule) .margin({ top: 10 }) .backgroundColor('#0D9FFB') .width(250) .height(40) .onClick(() => { // 通过按钮申请长时任务 this.startContinuousTask(); })
Button() { Text('取消长时任务').fontSize(25).fontWeight(FontWeight.Bold) } .type(ButtonType.Capsule) .margin({ top: 10 }) .backgroundColor('#0D9FFB') .width(250) .height(40) .onClick(() => { // 通过按钮取消长时任务 this.stopContinuousTask(); })
Button() { Text('开始录制').fontSize(25).fontWeight(FontWeight.Bold) } .type(ButtonType.Capsule) .margin({ top: 10 }) .backgroundColor('#0D9FFB') .width(250) .height(40) .onClick(async () => { this.startRecording(); })
Button() { Text('暂停录制').fontSize(25).fontWeight(FontWeight.Bold) } .type(ButtonType.Capsule) .margin({ top: 10 }) .backgroundColor('#0D9FFB') .width(250) .height(40) .onClick(() => { this.stopRecording(); }) } .width('100%') } .height('100%') }
private screenCapture?: media.AVScreenCaptureRecorder;
// 调用startRecording方法可以开始一次录屏存文件的流程,结束录屏可以通过点击录屏胶囊停止按钮进行操作。 public async startRecording() { this.screenCapture = await media.createAVScreenCaptureRecorder(); console.info(this.TAG,"startRecording screenCapture on done" ); try { let avCaptureConfig: media.AVScreenCaptureRecordConfig = { fd: this.mFile?.fd ?? 0, // 文件需要先有调用者创建,赋予写权限,将文件fd传给此参数 } await this.screenCapture?.init(avCaptureConfig); // avCaptureConfig captureConfig console.info(this.TAG,"startRecording screenCapture init done" ); } catch (err) { console.info(this.TAG,"startRecording init err: " + JSON.stringify(err) ); } await this.screenCapture?.startRecording(); console.info(this.TAG,"startRecording screenCapture startRecording" ); }
// 可以主动调用stopRecording方法来停止录屏。 public async stopRecording() { if (this.screenCapture == undefined) { // Error return; } await this.screenCapture?.stopRecording();
// 调用release()方法销毁实例,释放资源。 await this.screenCapture?.release();
// 最后需要关闭创建的录屏文件fd, fs.close(fd); fs.closeSync(this.mFile); }}
复制代码


发布于: 刚刚阅读数: 5
用户头像

GeorgeGcs

关注

路漫漫其修远兮,吾将上下而求索。 2024-12-24 加入

历经腾讯,宝马,研究所,金融。 待过私企,外企,央企。 深耕大应用开发领域十年。 OpenHarmony,HarmonyOS,Flutter,H5,Android,IOS。 目前任职鸿蒙应用架构师。 HarmonyOS官方认证创作先锋

评论

发布
暂无评论
【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务,解决屏幕录制音乐播放等操作不被挂起_后台任务_GeorgeGcs_InfoQ写作社区