当前位置: 首页 > news >正文

【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和abilityNamewants: [{bundleName: "com.test.basedemo",abilityName: "EntryAbility"}],// 指定点击通知栏消息后的动作是拉起abilityactionType: 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';

struct 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/fileslet 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和abilityNamewants: [{bundleName: "com.test.basedemo",abilityName: "EntryAbility"}],// 指定点击通知栏消息后的动作是拉起abilityactionType: 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 captureConfigconsole.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) {// Errorreturn;}await this.screenCapture?.stopRecording();// 调用release()方法销毁实例,释放资源。await this.screenCapture?.release();// 最后需要关闭创建的录屏文件fd, fs.close(fd);fs.closeSync(this.mFile);}
}

相关文章:

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

【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务,解决屏幕录制音乐播放等操作不被挂起 一、前言 1.后台是什么? 了解后台任务和长时任务前,我们需要先明白鸿蒙的后台特性:所谓的后台,指的是设备返回主界面、锁屏、…...

STM32-WWDG/IWDG看门狗

WWDG/IWDG一旦开启不能关闭,可通过选项字节在上电时启动硬件看门狗,看门狗计数只能写入不能读取。看门狗启用时,T6bit必须置1,防止立即重置。 一、原理 独立看门狗-超时复位 窗口看门狗-喂狗(重置计数器,…...

基于视觉惯性 SLAM(VSLAM)、相机和 IMU 数据的融合执行 6 自由度位姿跟踪

案例来源:https://spectacularai.github.io/docs/sdk/wrappers/oak.html 适配相机:带IMU的 OAK-D 系列相机 基于视觉惯性 SLAM(VSLAM)、相机和 IMU 数据的融合执行 6 自由度位姿跟踪 ~~~~~~~(分界线)~~~~~…...

Matlab仿真径向受压圆盘光弹图像

Matlab仿真径向受压圆盘光弹图像-十步相移法 主要参数 % 定义圆盘参数 R 15; % 圆盘半径,单位:mm h 5; % 圆盘厚度,单位:mm P 300; % 径向受压载荷大小,单位&#xff…...

网络安全抓包

#知识点: 1、抓包技术应用意义 //有些应用或者目标是看不到的,这时候就要进行抓包 2、抓包技术应用对象 //app,小程序 3、抓包技术应用协议 //http,socket 4、抓包技术应用支持 5、封包技术应用意义 总结点:学会不同对象采用…...

WebSocket 测试调试:工具与实践

在前五篇文章中,我们深入探讨了 WebSocket 的基础原理、服务端开发、客户端实现、安全实践和性能优化。今天,让我们把重点放在测试和调试上,看看如何确保 WebSocket 应用的质量和可靠性。我曾在一个实时通讯项目中,通过完善的测试和调试策略,将线上问题的发现时间从小时级缩短到…...

ArmSoM RK3588/RK3576核心板,开发板网络设置

ArmSoM系列产品都搭配了以太网口或WIFI模块,PCIE转以太网模块、 USB转以太网模块等,这样我们的网络需求就不止是上网这么简单了,可以衍生出多种不同的玩法。 1. 网络连接​ 连接互联网或者组成局域网都需要满足一个前提–设备需要获取到ip&a…...

【学Rust开发CAD】1 环境搭建

文章目录 一、搭建C/C编译环境二、安装Rust三、配置 PATH 环境变量四、验证安装结果五、安装编辑工具 一、搭建C/C编译环境 Rust 的编译工具依赖 C 语言的编译工具,这意味着你的电脑上至少已经存在一个 C 语言的编译环境。如果你使用的是 Linux 系统,往…...

数据结构与算法之二叉树: LeetCode 108. 将有序数组转换为二叉搜索树 (Ts版)

将有序数组转换为二叉搜索树 https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/description/ 描述 给你一个整数数组 nums ,其中元素已经按 升序 排列请你将其转换为一棵 平衡 二叉搜索树 示例 1 输入:nums [-10,-3,0,5,9…...

Java 多线程之@Async

SpringBoot 中使用 Async 使用 Async 注解步骤: 添加 EnableAsync 注解。在主类上或者 某个类上,否则,异步方法不会生效 添加 Async注解。在异步方法上添加此注解。异步方法不能被 static 修饰需要自定义线程池,则可以配置线程池…...

代码随想录day38 动态规划6

题目:322.零钱兑换 279.完全平方数 139.单词拆分 多重背包 背包总结 需要重做:322,139 322. 零钱兑换 思路:零钱,可取多次-》完全背包。 注意: 五部: 1.dp[j]:价值为j的时候,最…...

LabVIEW无标题的模态VI窗口的白框怎么去除?

在LabVIEW中,如果你遇到无标题的模态(modal)VI窗口显示有一个白框,通常是由于VI界面的一些属性或者控件设置问题导致的。为了去除这个白框,可以尝试以下几种方法: 1. 检查VI窗口属性设置 确保你的VI窗口属…...

iOS - 原子操作

在 Objective-C 运行时中,原子操作主要通过以下几种方式实现: 1. 基本原子操作 // 原子操作的基本实现 #if __has_feature(c_atomic)#define OSAtomicIncrement32(p) __c11_atomic_add((_Atomic(int32_t) *)(p), 1, __ATOMIC_RELAXED) #define …...

Go语言的语法

Go语言入门与实战 引言 在当今的开发环境中,随着互联网的快速发展,程序员们面临着越来越复杂的系统需求。针对这些需求,Go语言(又称Golang)作为一种新的编程语言应运而生。Go语言由Google开发,它具有简单…...

【MySQL 保姆级教学】用户管理和数据库权限(16)

数据库账户管理是指对数据库用户进行创建、修改和删除等操作,以控制用户对数据库的访问权限。通过账户管理,可以设置用户名、密码、主机地址等信息,确保数据库的安全性和可控性。例如,使用 CREATE USER 创建用户,ALTER…...

什么是 ES6 “模板语法” ?

ES6 提出了“模板语法”的概念。在 ES6 以前,拼接字符串是很麻烦的事情 var name css var career coder! var hobby [coding ,"writing] var finalString my name is name ,I work as a career I love hobby[0] and hobby[1]仅仅几…...

[项目实战2]贪吃蛇游戏

目录 贪吃蛇游戏:: 一、游戏效果及功能实现: 1.规则 ​​​​​​​ ​​​​​​​ ​​​​​​​ 2.基本功能实现 ​​​​​​​ ​​​​​​​ ​​​​​​​ 3.技术要点 ​​​​​​​…...

关于FPGA中添加FIR IP核(采用了GOWIN EDA)

文章目录 前言一、IP核二、MATLAB文件三、导出系数COE文件1.设计滤波器2.用官方的matlab代码或者直接用文本文件 四、进行模块化设计源文件 前言 FIR滤波器的特点是其输出信号是输入信号的加权和,权值由滤波器的系数决定。每个系数代表了滤波器在特定延迟位置上的“…...

1. 使用springboot做一个音乐播放器软件项目【前期规划】

背景: 现在大部分音乐软件都是要冲会员才可以无限常听的。对于喜欢听音乐的小伙伴,资金又比较紧张,是那么的不友好。作为程序员的我,也是喜欢听着歌,敲着代码。 最近就想做一个音乐播放器的软件,在内网中使…...

【Dify】Dify自定义模型设置 | 对接DMXAPI使用打折 Openai GPT 或 Claude3.5系列模型方法详解

一、Dify & DMXAPI 1、Dify DIFY(Do It For You)是一种自动化工具或服务,旨在帮助用户简化操作,减少繁琐的手动操作,提升工作效率。通过DIFY,用户能够快速完成任务、获取所需数据,并且可以…...

【kafka】Golang实现分布式Masscan任务调度系统

要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

深度学习习题2

1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...

云安全与网络安全:核心区别与协同作用解析

在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...