OpenHarmony实战开发-合理运行后台任务

简介

设备返回主界面、锁屏、应用切换等操作会使应用退至后台。为了降低设备耗电速度、保障用户使用流畅度,系统会对退至后台的应用进行管控,包括进程挂起和进程终止。为了保障后台音乐播放、日历提醒等功能的正常使用,系统提供了受规范约束的后台任务,扩展应用在后台的运行时间。本文将介绍各类后台任务的基本概念和适用场景,并且通过对短时任务和长时任务两个场景的性能分析说明合理运行后台任务的必要性。

短时任务

应用退至后台一小段时间后,应用进程会被挂起,无法执行对应的任务。如果应用在后台仍需要执行耗时不长的任务,可以申请短时任务,扩展应用在后台的运行时间。

短时任务适用于小文件下载、缓存、信息发送等实时性高、需要临时占用资源执行的任务。详细的开发指导可参考短时任务。

场景示例

下面代码在申请短时任务后执行了一个耗时计算任务。

import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import { BusinessError } from '@ohos.base';
import util from '@ohos.util';
import hiTraceMeter from '@ohos.hiTraceMeter';

const totalTimes: number = 50000000; // 循环次数
const calculateResult: string = 'Total time costed = %s ms.'; // 文本格式

@Entry
@Component
struct Index {
  @State message: string = 'Click button to calculate.';
  private requestId: number = 0;

  // 申请短时任务
  requestSuspendDelay() {
    try {
      let delayInfo = backgroundTaskManager.requestSuspendDelay('compute', () => {
        console.info('Request suspension delay will time out.');
        // 任务即将超时,取消短时任务
        this.cancelSuspendDelay();
      })
      this.requestId = delayInfo.requestId;
    } catch (error) {
      console.error(`requestSuspendDelay failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
    }
  }

  // 取消短时任务
  cancelSuspendDelay() {
    backgroundTaskManager.cancelSuspendDelay(this.requestId);
    console.info('Request suspension delay cancel.');
  }

  // 计算任务
  computeTask(times: number): number {
    let start: number = new Date().getTime();
    let a: number = 1;
    let b: number = 1;
    let c: number = 1;
    for (let i: number = 0; i < times; i++) {
      a = a * Math.random() + b * Math.random() + c * Math.random();
      b = a * Math.random() + b * Math.random() + c * Math.random();
      c = a * Math.random() + b * Math.random() + c * Math.random();
    }
    let end: number = new Date().getTime();
    return end - start;
  }

  // 点击回调
  clickCallback = () => {
    this.requestSuspendDelay();
    hiTraceMeter.startTrace('computeTask', 0); // 开启性能打点
    let timeCost = this.computeTask(totalTimes);
    this.message = util.format(calculateResult, timeCost.toString());
    hiTraceMeter.finishTrace('computeTask', 0); // 结束性能打点
    this.cancelSuspendDelay();
  }

  build() {
    Column() {
      Row(){
        Text(this.message)
      }
      Row() {
        Button('开始计算')
          .onClick(this.clickCallback)
      }
      .width('100%')
      .justifyContent(FlexAlign.Center)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

使用 IDE 中的 Time Profiler 获取示例应用从开始计算任务并退到后台执行一分钟内的性能数据。获取到的数据如下图。

图1 短时任务 Time Profiler 泳道图
在这里插入图片描述

  • ArkTS Callstack:基于时间轴展示 CPU 占用率和状态的变化。
  • User Trace:基于时间轴展示当前时段内触发用户自定义打点任务的具体情况。H:computeTask 表示短时任务执行用时。
  • Native Callstack:基于时间轴展示 CPU 占用率变化和进程/线程的活动状态以及函数调用栈。

从上图中可以看出,Native Callstack 泳道与 H:computeTask 相对应的时间段内应用进程处于活跃状态,CPU 占用率在较高范围内变化。任务取消后,应用仍然处于运行状态,但是进程的活跃程度和 CPU 占用率都明显下降,直到在几秒后系统将应用挂起,不再占用 CPU。

分别框选任务执行阶段和任务取消后未被挂起阶段对应的 Native Callstack 如下图,查看应用主线程在两个阶段的平均 CPU 占用率和最高 CPU 占用率情况。

图2 任务执行阶段的 CPU 占用率
在这里插入图片描述

图3 任务取消后未被挂起阶段的 CPU 占用率
在这里插入图片描述

可以看到应用主线程在任务执行阶段的平均 CPU 占用率为 12.6%,最高 CPU 占用率为 40.0%,在任务取消后未被挂起阶段的平均 CPU 占用率为 2.2%,最高 CPU 占用率为 28.6%。

在后台运行短时任务,会占用系统 CPU,在后台执行过多的短时任务就有可能会导致前台的应用卡顿,因此建议非必要情况不使用短时任务,使用时也避免同时申请过多的短时任务。

更多短时任务的使用限制和注意事项可以参考短时任务约束与限制。

长时任务

应用退至后台后,在后台需要长时间运行用户可感知的任务,如播放音乐、导航等。为防止应用进程被挂起,导致对应功能异常,可以申请长时任务,使应用在后台长时间运行。申请长时任务后,系统会做相应的校验,确保应用在执行相应的长时任务。详细的开发指导可参考长时任务。

长时任务支持的类型包括数据传输、音视频播放、录音、定位导航、蓝牙相关、多设备互联、WLAN 相关、音视频通话、计算任务。可以根据下表的场景举例选择相应的长时任务类型。
在这里插入图片描述

  • 申请了数据传输的长时任务,系统仅会提升应用进程的优先级,降低系统终止应用进程的概率,但仍然会挂起对应的应用进程。对于上传下载对应的功能,需要调用系统上传下载代理接口托管给系统执行,可以参考文件上传下载性能提升指导。
  • 申请音视频播放长时任务必须使用媒体会话服务,否则无法在后台播放。
  • 申请录音类型长时任务,需要有显著的用户提示,必须通过动态授权弹框来提供用户授权界面,请求用户授权麦克风权限。

场景示例

下面模拟一个后台定位的场景。应用订阅设备位置变化,每隔一秒获取位置信息,为了保证应用在退到后台后仍然可以使用定位服务,申请了定位类型的长时任务。

首先需要在 module.json5 配置文件中为需要使用长时任务的 EntryAbility 声明任务类型。

{
  "module": {
    ...
    "abilities": [
      {
        "name": "EntryAbility",
        ...
        "backgroundModes": [
          "location"
        ]
      }
    ],
  }
}

需要使用到的相关权限如下:

  • ohos.permission.INTERNET
  • ohos.permission.LOCATION_IN_BACKGROUND
  • ohos.permission.APPROXIMATELY_LOCATION
  • ohos.permission.LOCATION
  • ohos.permission.KEEP_BACKGROUND_RUNNING

权限申请方式参考配置文件权限申明,在 module.json5 中进行配置。其中部分权限申请以及打开使能通知开关需要用户手动确认。系统为申请的长时任务发布通知栏消息时,应用的使能通知开关必须处于开启状态,否则用户无法感知后台正在运行的长时任务。

后台定位的实现代码如下:

import wantAgent, { WantAgent } from '@ohos.app.ability.wantAgent';
import common from '@ohos.app.ability.common';
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import { BusinessError } from '@ohos.base';
import geolocation from '@ohos.geoLocationManager';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import notificationManager from '@ohos.notificationManager';

const TAG: string = 'BackgroundLocation';

@Entry
@Component
export struct LongTermTaskView {
  @State latitude: number = 0;
  @State longitude: number = 0;

  aboutToAppear() {
    // 请求发送通知的许可
    notificationManager.requestEnableNotification().then(() => {
      console.info(`[EntryAbility] requestEnableNotification success`);
      // 申请定位相关权限
      let atManager = abilityAccessCtrl.createAtManager();
      try {
        atManager.requestPermissionsFromUser(getContext(this), ['ohos.permission.INTERNET',
          'ohos.permission.LOCATION',
          'ohos.permission.LOCATION_IN_BACKGROUND',
          'ohos.permission.APPROXIMATELY_LOCATION'])
          .then((data) => {
            console.info(`[EntryAbility], data: ${JSON.stringify(data)}`);
          })
          .catch((err: BusinessError) => {
            console.info(`[EntryAbility], err: ${JSON.stringify(err)}`);
          })
      } catch (err) {
        console.info(`[EntryAbility], catch err->${JSON.stringify(err)}`);
      }
    }).catch((err: BusinessError) => {
      console.error(`[EntryAbility] requestEnableNotification failed, code is ${err.code}, message is ${err.message}`);
    });
  }

  // 位置变化回调
  locationChange = async (location: geolocation.Location) => {
    console.info(TAG, `locationChange location =${JSON.stringify(location)}`);
    this.latitude = location.latitude;
    this.longitude = location.longitude;
  }

  // 获取定位
  async getLocation() {
    console.info(TAG, `enter getLocation`);
    let requestInfo: geolocation.LocationRequest = {
      priority: geolocation.LocationRequestPriority.FIRST_FIX, // 快速获取位置优先
      scenario: geolocation.LocationRequestScenario.UNSET, // 未设置场景信息
      timeInterval: 1, // 上报位置信息的时间间隔
      distanceInterval: 0, // 上报位置信息的距离间隔
      maxAccuracy: 100 // 精度信息
    };
    console.info(TAG, `on locationChange before`);
    geolocation.on('locationChange', requestInfo, this.locationChange);
    console.info(TAG, `on locationChange end`);
  }

  // 开始长时任务
  startContinuousTask() {
    let context: Context = getContext(this);
    // 通知参数,指定点击长时任务通知后跳转的应用
    let wantAgentInfo: wantAgent.WantAgentInfo = {
      wants: [
        {
          bundleName: (context as common.UIAbilityContext).abilityInfo.bundleName,
          abilityName: (context as common.UIAbilityContext).abilityInfo.name
        }
      ],
      operationType: wantAgent.OperationType.START_ABILITY,
      requestCode: 0,
      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    };
    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
      backgroundTaskManager.startBackgroundRunning(context,
        backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj).then(() => {
        console.info(`Succeeded in operationing startBackgroundRunning.`);
      }).catch((err: BusinessError) => {
        console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
      });
    });
  }

  // 停止长时任务
  stopContinuousTask() {
    backgroundTaskManager.stopBackgroundRunning(getContext()).then(() => {
      console.info(`Succeeded in operationing stopBackgroundRunning.`);
    }).catch((err: BusinessError) => {
      console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
    });
  }

  build() {
    Column() {
      Column() {
        Text(this.latitude.toString())
        Text(this.longitude.toString())
      }
      .width('100%')

      Column() {
        Button('开启定位服务')
          .onClick(() => {
            this.startContinuousTask();
            this.getLocation();
          })
        Button('关闭定位服务')
          .onClick(async () => {
            await geolocation.off('locationChange');
            this.stopContinuousTask();
          })
          .margin({ top: 10 })
      }
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

基于上述场景,使用功耗测试工具获取 30min 设备功耗,得到的数据如下表。
在这里插入图片描述

  • 归一电流:电压处于 3.8V 时的电流平均值。
  • 归一耗电的计算方式为:归一电流*测试时长/3600。3600 表示一小时的秒数。

对比后台存在长时定位任务和不存在长时任务时的功耗数据,当后台存在定位任务持续运行时,设备在 30 分钟内的功耗明显增加。

从功耗角度考虑,应用应该避免过多使用长时任务,针对必须使用长时任务的场景,也可以优化任务执行过程,减少设备功耗。以下是一些优化建议:

  • 对定位要求不太高的场景可以适当调整上报时间间隔和上报距离间隔,减少更新频率。
  • 尽可能的减少网络请求次数和减小网络请求时间间隔。
  • 数据传输中使用高效率的数据格式和解析方法,减少任务执行时间。

更多长时任务的使用限制和注意事项可以参考长时任务约束与限制。

延迟任务

应用退至后台后,如果需要执行实时性要求不高的任务,可以使用延迟任务。当应用满足设定条件(包括网络类型、充电类型、存储状态、电池状态、定时状态等)时,将任务添加到执行队列,系统会根据内存、功耗、设备温度、用户使用习惯等统一调度拉起应用。

延迟任务适用于软件更新、信息收集、数据处理等场景。详细的开发指导可参考延迟任务。

代理提醒

应用退到后台或进程终止后,仍然有一些提醒用户的定时类任务,例如购物类应用抢购提醒等,为满足此类功能场景,系统提供了代理提醒(reminderAgentManager)的能力。当应用退至后台或进程终止后,系统会代理应用做相应的提醒。详细的开发指导可参考代理提醒。

当前支持的提醒类型包括:

  • 倒计时:基于倒计时的提醒功能,适用于短时的计时提醒场景,例如抢购倒计时。
  • 日历:基于日历的提醒功能,适用于较长时间的日程提醒场景,例如生日提醒。
  • 闹钟:基于时钟的提醒功能,适用于闹钟相关场景,例如起床闹钟。

总结

合理的选择和使用后台任务对于优化用户体验,减少性能消耗非常重要。以下表格对比总结了各类后台任务的概念、适用场景以及任务执行过程中的应用状态。

在这里插入图片描述

如果大家还没有掌握鸿蒙,现在想要在最短的时间里吃透它,我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程》以及《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

OpenHarmony APP开发教程步骤:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

《鸿蒙开发学习手册》:

如何快速入门:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.基本概念
2.构建第一个ArkTS应用
3.……

在这里插入图片描述

开发基础知识:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙生态应用开发白皮书V2.0PDF:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/568850.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

iptables实现docker容器动态端口映射实操

背景 之前在《Docker 动态修改容器端口映射的方法》一文中&#xff0c;说明了如何使用修改配置和加防火墙规则实现动态端口映射。但是没有具体分享加防火墙实现动态端口映射的实际案例。今天就分享一下实际操作案例&#xff0c;供大家参考。 分析 动态端口映射的用途 容器端口…

JavaEE初阶——多线程(六)——线程池

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享多线程的第六篇文章,关于线程池 如果有不足的或者错误的请您指出! 目录 3.线程池3.1标准库的线程池3.2 标准库自己提供的几个工厂类3.3自己实现一个线程池完成大体框架接下来完…

解决DataGrip连接MySQL8时出现时区错误问题

解决办法&#xff1a;在url后面拼接时区参数 ?serverTimezoneAsia/Shanghai

生成式AI在B端产品的应用分析

AI产品发展到现在&#xff0c;消费端的产品应用还受到比较大的限制&#xff1b;但是在B端&#xff0c;已经有了不错的表现。作者总结了AI产品在B端的几款应用&#xff0c;一起来看看表现如何。 生成式AI在B端产品的应用分析© 由 ZAKER 提供 随着今年生成式AI应用的大范围…

绝地求生:16款战术手套,你最钟爱哪一款?

大家好&#xff0c;我是闲游盒&#xff01; 喜迎PUBG七周年生日同时游戏里又迎来了一款新的战术手套&#xff0c;那么就让我们来回顾一下目前出游戏中的16款战术手套吧&#xff0c;看看你最中意的是哪一款&#xff1f; 1、MAZARIN1K 战术手套 2、SPAJKK 战术手套 3、SWAGGER 战…

机器学习(四)之无监督学习

前言&#xff1a; 前面写了监督学习的几种算法&#xff0c;下面就开始无监督啦&#xff01; 如果文章有错误之处&#xff0c;小伙伴尽情在评论区指出来&#xff08;嘿嘿&#xff09;&#xff0c;看到就会回复的。 1.聚类&#xff08;Clustering&#xff09; 1.1 概述&#xff…

8.4.3 使用3:配置单臂路由实现VLAN间路由

1、实验目的 通过本实验可以掌握&#xff1a; 路由器以太网接口上的子接口配置和调试方法。单臂路由实现 VLAN间路由的配置和调试方法。 2、实验拓扑 实验拓扑如下图所示。 3、实验步骤 &#xff08;1&#xff09;配置交换机S1 S1(config)#vlan 2 S1(config-vlan)#exit S…

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第七套

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第七套 (共9套&#xff0c;有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&#xff0c;仅供参考&#xff09; 部分题目分享&#xff0c;完整版获取&#xff08;WX:didadidadidida313&#xff0c;加我备注&#x…

LayuiMini使用时候初始化模板修改(下载源码)

忘记加了 下载 地址 &#xff1a; layui-mini: layuimini&#xff0c;后台admin前端模板&#xff0c;基于 layui 编写的最简洁、易用的后台框架模板。只需提供一个接口就直接初始化整个框架&#xff0c;无需复杂操作。 LayuiMini使用时候初始化模板官网给的是&#xff1a; layu…

立即刷新导致请求的response没有来得及加载造成的this request has no response data available

1、前端递归调用后端接口 const startProgress () > {timer.value setInterval(() > {if (progress.value < 100) {time.value--;progress.value Math.ceil(100 / wait_time.value);} else {clearInterval(timer.value);progress.value 0;timer.value null;time.…

电磁兼容(EMC):静电放电(ESD)抗扰度试验深度解读(六)

目录 1. 静电测试干扰方式 2. 案例一 3. 案例二 4. 案例三 5. 案例四 6. 总结 静电放电测试的复杂性决定了这项测试对产品的主要影响方式也是多样的。标准里介绍了几种常见的影响方式&#xff1a; 1. 静电测试干扰方式 在静电放电试验中&#xff0c;测试了受试设备对于…

CDN、边缘计算与云计算:构建现代网络的核心技术

在数字化时代&#xff0c;数据的快速传输和处理是保持竞争力的关键。内容分发网络&#xff08;CDN&#xff09;、边缘计算和云计算共同构成了现代互联网基础架构的核心&#xff0c;使内容快速、安全地到达用户手中。本文将探讨这三种技术的功能、相互关系以及未来的发展趋势。 …

大语言模型微调过程中的 RLHF 和 RLAIF 有什么区别?

目前想要深入挖掘大型语言模型&#xff08;LLM&#xff09;的全部潜力需要模型与我们人类的目标和偏好保持一致。从而出现了两种方法&#xff1a;来自人类反馈的人力强化学习&#xff08;RLHF&#xff09;和来自人工智能反馈的人工智能驱动的强化学习&#xff08;RLAIF&#xf…

rosdep一键修复

External Player - 哔哩哔哩嵌入式外链播放器 rosdep失败原因 通常在执行rosdep init操作时就会报错&#xff0c;问题的核心在于rosdep会访问raw.githubusercontent.com这个网址下的资源&#xff0c;例如https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/sour…

免费开源线上社交交友婚恋系统平台 可打包小程序 支持二开 源码交付!

婚姻是人类社会中最重要的关系之一&#xff0c;它对个人和家庭都有着深远的影响。然而&#xff0c;在现代社会的快节奏生活中&#xff0c;找到真爱变得越来越困难。在这个时候&#xff0c;婚恋产品应运而生&#xff0c;为人们提供了寻找真爱的新途径。 1.拓宽人际交流圈子 现代…

【Camera KMD ISP SubSystem笔记】CRM V4L2驱动模型

1. CRM为主设备 /dev/video0&#xff0c;先创建 v4l2_device 设备&#xff0c;再创建 video_device 设备&#xff0c;最后创建 media_device 设备/dev/media0 v4l2_device的mdev指向media_device&#xff0c;v4l2_device的entity链接到media_device的entities上&#xff08…

P1106 删数问题

本题为洛谷&#xff1a; #include<iostream> #include<string> using namespace std; int main(){string n;int k;cin>>n>>k;while(k--){for(int i0;i<n.length();i){if(n[i]>n[i1]){n.erase(i,1); break;} }for(int i0;i<n.length()-1&&…

SpringBoot学习之Kafka发送消费消息入门实例(三十五)

使用Kafka之前需要先启动fKafka,如何下载安装启动kafka请先参考本篇文章的前两篇: 《SpringBoot学习之Kafka下载安装和启动【Windows版本】(三十四)》 《SpringBoot学习之Kafka下载安装和启动【Mac版本】(三十三)》 一、POM依赖 1、加入kafka依赖 2、我的整个POM代码…

Jammy@Jetson Orin - Tensorflow Keras Get Started: 000 setup for tutorial

JammyJetson Orin - Tensorflow & Keras Get Started: 000 setup for tutorial 1. 源由2. 搭建环境2.1 安装IDE环境2.2 安装numpy2.3 安装keras2.4 安装JAX2.5 安装tensorflow2.6 安装PyTorch2.7 安装nbdiff 3. 测试DEMO3.1 numpy版本兼容问题3.2 karas API - model.compil…

Day 20 Linux的WEB服务——apache

WEB服务简介 目前主流的web服务器软件 Linux&#xff1a;apache &#xff0c; nginx Windows-server&#xff1a;IIS 服务器安装nginx或apache后&#xff0c;叫做web服务器&#xff08;又称WWW服务器&#xff09; web服务器软件属于C/S框架模型 web服务器是一种被动程序只…