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

鸿蒙实现后台任务管理

目录:

    • 1、后台任务类型
    • 2、应用后台运行
    • 3、后台任务类型实现
      • 3.1、短时任务
      • 3.2、长时任务
      • 3.3、延时任务
      • 3.4、能效资源

1、后台任务类型

OpenHarmony将后台任务分为四种类型,并提供了一个资源申请的扩展功能:

  • 无后台业务 :应用或业务模块退到后台后,无任务需要处理。
  • 短时任务:应用或业务模块退到后台后,如果有紧急不可推迟且短时间能完成的任务,如应用退后台要进行数据压缩,不可中断,则使用短时任务申请延迟进入挂起(Suspend)状态。
  • 长时任务:如果是用户发起的可感知业务需要长时间后台运行,如后台播放音乐、导航、设备连接、VoIP等,则使用长时任务避免进入挂起(Suspend)状态。
  • 延迟任务:延迟任务调度给应用提供一个机制,允许应用根据系统安排,在系统空闲时执行实时性不高的任务。当满足设定条件的时候,任务会被放入待调度队列,当系统空闲时调度该任务。
  • 能效资源 :能效资源包括CPU资源、WORK_SCHEDULER资源、软件资源(COMMON_EVENT,TIMER)、硬件资源(GPS,BLUETOOTH)。如果应用或者进程申请了能效资源,那么根据能效资源的类型会拥有相应的特权,例如申请了CPU资源的可以不被挂起,申请了WORK_SCHEDULER后延时任务可以拥有更长的执行时间。
    在这里插入图片描述

2、应用后台运行

minimize方法提供该能力。若主窗口调用,可以将窗口最小化,并支持在Dock栏中还原。若子窗口调用,可以将窗口隐藏。

参考代码如下:

在EntryAbility.ets的onWindowStageCreate回调中全局保存windowStage:

AppStorage.setOrCreate('context', windowStage);

在页面中获取windowStage并调用方法实现最小化:

import { window } from '@kit.ArkUI'; 
 
@Component 
export struct BackgroundExecution { 
  @State message: string = '后台运行'; 
 
  build() { 
    Column() { 
      Button(this.message) 
        .width('40%') 
        .onClick(() => { 
          let windowStage = AppStorage.get('context') as window.WindowStage; 
          windowStage.getMainWindowSync().minimize(); 
          //当应用进入后台时就可以对接我们后面的流程了,后台任务管理,如短时任务逻辑等        
          //这里实现后台任务逻辑
        }) 
    } 
    .height('100%') 
    .width('100%') 
    .justifyContent(FlexAlign.Center) 
  } 
}

3、后台任务类型实现

3.1、短时任务

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

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

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)
  }
}

3.2、长时任务

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

首先需要在 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

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

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)
  }
}

3.3、延时任务

实现延迟任务回调拓展能力:

import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility';
import workScheduler from '@ohos.resourceschedule.workScheduler';
export default class MyWorkSchedulerExtensionAbility extends WorkSchedulerExtensionAbility {
  // 延迟任务开始回调
  onWorkStart(workInfo: workScheduler.WorkInfo) {
    console.info(`onWorkStart, workInfo = ${JSON.stringify(workInfo)}`);
  }

  // 延迟任务结束回调
  onWorkStop(workInfo: workScheduler.WorkInfo) {
    console.info(`onWorkStop, workInfo is ${JSON.stringify(workInfo)}`);
  }
}
{
  "module": {
      "extensionAbilities": [
        {
          "name": "MyWorkSchedulerExtensionAbility",
          "srcEntry": "./ets/WorkSchedulerExtension/WorkSchedulerExtension.ets",
          "label": "$string:WorkSchedulerExtensionAbility_label",
          "description": "$string:WorkSchedulerExtensionAbility_desc",
          "type": "workScheduler"
        }
      ]
  }
}

实现延迟任务调度:

import workScheduler from '@ohos.resourceschedule.workScheduler';
import { BusinessError } from '@ohos.base';
// 创建workinfo
const workInfo: workScheduler.WorkInfo = {
  workId: 1,
  networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI,
  bundleName: 'com.example.application',
  abilityName: 'MyWorkSchedulerExtensionAbility'
}

try {
  workScheduler.startWork(workInfo);
  console.info(`startWork success`);
} catch (error) {
  console.error(`startWork failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
// 创建workinfo
const workInfo: workScheduler.WorkInfo = {
  workId: 1,
  networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI,
  bundleName: 'com.example.application', 
  abilityName: 'MyWorkSchedulerExtensionAbility' 
}

try {
  workScheduler.stopWork(workInfo);
  console.info(`stopWork success`);
} catch (error) {
  console.error(`stopWork failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}

3.4、能效资源

import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';

// 申请能效资源
let request = {
    resourceTypes: backgroundTaskManager.ResourceType.COMMON_EVENT|
        backgroundTaskManager.ResourceType.TIMER,
    isApply: true,
    timeOut: 0,
    reason: "apply",
    isPersist: true,
    isProcess: true,
};

let res;
try {
    res = backgroundTaskManager.applyEfficiencyResources(request);
    console.info("the result of request is: " + res);
} catch (error) {
    console.error(`Operation applyEfficiencyResources failed. code is ${error.code} message is ${error.message}`);
}

// 释放部分资源
request = {
    resourceTypes: backgroundTaskManager.ResourceType.COMMON_EVENT,
    isApply: false,
    timeOut: 0,
    reason: "reset",
    isPersist: true,
    isProcess: true,
};
try {
    res = backgroundTaskManager.applyEfficiencyResources(request);
    console.info("the result of request is: " + res);
} catch (error) {
    console.error(`Operation applyEfficiencyResources failed. code is ${error.code} message is ${error.message}`);
}

// 释放全部资源
try {
    backgroundTaskManager.resetAllEfficiencyResources();
} catch (error) {
    console.error(`Operation resetAllEfficiencyResources failed. code is ${error.code} message is ${error.message}`);
}
  // 使用示例也是一样
  clickCallback = () => {
    backgroundTaskManager.applyEfficiencyResources(request);//申请能效资源
    hiTraceMeter.startTrace('computeTask', 0); // 开启性能打点
    let timeCost = this.computeTask(totalTimes);//进行任务逻辑处理
    this.message = util.format(calculateResult, timeCost.toString());
    hiTraceMeter.finishTrace('computeTask', 0); // 结束性能打点
    //任务逻辑处理完释放资源,下面是释放部分资源和释放全部资源,这里根据业务二选一即可
    backgroundTaskManager.applyEfficiencyResources(request);
    backgroundTaskManager.resetAllEfficiencyResources();
  }

http://www.kler.cn/a/429829.html

相关文章:

  • 极大似然估计笔记
  • [程序设计]—代理模式
  • 【redis】ubuntu18安装redis7
  • FPGA自学之路:到底有多崎岖?
  • 「Mac畅玩鸿蒙与硬件44」UI互动应用篇21 - 随机励志语录生成器
  • Springboot3整合Redis
  • Fyne ( go跨平台GUI )中文文档-Fyne总览(二)
  • gitlab配置调试minio
  • Java将数组转换成字符串
  • 构建万能 MOCK-API
  • 如何在拉丁美洲推广游戏
  • docker逃逸总结
  • vue+elementUI+transition实现鼠标滑过div展开内容,鼠标划出收起内容,加防抖功能
  • docker搭建elasticsearch服务
  • python爬虫--小白篇【爬虫实践】
  • R 语言科研绘图第 4 期 --- 折线图-置信区间
  • 一种基于通义千问prompt辅助+Qwen2.5-coder-32b+Bolt.new+v0+Cursor的无代码对话网站构建方法
  • 使用 RabbitMQ 创建简单消费者的完整指南
  • 什么是Layer Normalization?
  • SpringBoot下类加入容器的几种方式
  • K8S命令部署后端(流水线全自动化部署)
  • P2249 【深基13.例1】查找
  • 2.linux中调度kettle
  • React - useActionState、useFormStatus与表单处理