一、Background Tasks Kit簡介

1.1 功能介紹

設備返回主界面、鎖屏、應用切換等操作會使應用退至後台。應用退至後台後,如果繼續活動,可能會造成設備耗電快、用户界面卡頓等現象。為了降低設備耗電速度、保障用户使用流暢度,系統會對退至後台的應用進行管控,包括進程掛起和進程終止。典型場景包括:應用退至後台一段時間應用進程會被掛起、資源不足時系統會終止部分應用進程(即回收該進程的所有資源)。掛起後,應用進程無法使用軟件資源(如公共事件、定時器等)和硬件資源(CPU、網絡、GPS、藍牙等)。
為了保障後台音樂播放、日曆提醒等功能的正常使用,Background Tasks Kit提供了規範內受約束的後台任務,擴展應用在後台的運行時間。

1.2 約束與限制

資源使用約束:對於運行的進程,系統會給予一定的資源配額約束,包括進程在連續一段時間內內存的使用、CPU使用佔比,以及24小時磁盤寫的IO量,均有對應的配額上限。超過配額上限時,如果進程處於前台,系統會有對應的warning日誌,如果進程處於後台,系統會終止該進程。

1.3 後台任務類型

Background Tasks Kit提供了規範內受約束的後台任務,包括短時任務、長時任務、延遲任務、代理提醒。
開發者可以根據如下的功能介紹,選擇合適的後台任務,以滿足應用退至後台後繼續運行的需求。

  • 短時任務:適用於實時性要求高、耗時不長的任務,例如狀態保存。
  • 長時任務:適用於長時間運行在後台、用户可感知的任務,例如後台播放音樂、導航、設備連接等,使用長時任務避免應用進程被掛起。
  • 延遲任務:對於實時性要求不高、可延遲執行的任務,系統提供了延遲任務,即滿足條件的應用退至後台後被放入執行隊列,系統會根據內存、功耗等統一調度。
  • 代理提醒:代理提醒是指應用退後台或進程終止後,系統會代理應用做相應的提醒。適用於定時提醒類業務,當前支持的提醒類型包括倒計時、日曆和鬧鐘三類。
後台任務

HarmonyOS:短時任務(ArkTS)_HarmonyOS

説明 系統僅支持規範內受約束的後台任務。應用退至後台後,若未使用規範內的後台任務或選擇的後台任務類型不正確,對應的應用進程會被掛起或終止。
應用申請了規範內的後台任務,僅會提升應用進程被回收的優先級。當系統資源嚴重不足時,即使應用進程申請了規範內的後台任務,系統仍會終止部分進程,用以保障系統穩定性。

二、短時任務(ArkTS)

2.1 概述

應用退至後台一小段時間後,應用進程會被掛起,無法執行對應的任務。如果應用在後台仍需要執行耗時不長的任務,如狀態保存等,可以通過本文申請短時任務,擴展應用在後台的運行時間。

2.2 約束與限制
  • 申請時機:應用需要在前台或onBackground回調內,申請短時任務,否則會申請失敗。
  • 數量限制:一個應用同一時刻最多申請3個短時任務。以圖1為例,在①②③時間段內的任意時刻,應用申請了2個短時任務;在④時間段內的任意時刻,應用申請了1個短時任務。
  • 配額機制:一個應用會有一定的短時任務配額(根據系統狀態和用户習慣調整),單日(24小時內)配額默認為10分鐘,單次配額最大為3分鐘,低電量時單次配額默認為1分鐘,配額消耗完後不允許再申請短時任務。同時,系統提供獲取對應短時任務剩餘時間的查詢接口,用以查詢本次短時任務剩餘時間,以確認是否繼續運行其他業務。
  • 配額計算:僅當應用在後台時,對應用下的短時任務計時;同一個應用下的同一個時間段的短時任務,不重複計時。以下圖為例:應用有兩個短時任務A和B,在前台時申請短時任務A,應用退至後台後開始計時為①,應用進入前台②後不計時,再次進入後台③後開始計時,短時任務A結束後,由於階段④仍然有短時任務B,所以該階段繼續計時。因此,在這個過程中,該應用短時任務總耗時為①+③+④。
短時任務配額計算原理圖

HarmonyOS:短時任務(ArkTS)_HarmonyOS_02

説明 任務完成後,應用需主動取消短時任務,否則會影響應用當日短時任務的剩餘配額。

  • 超時:短時任務即將超時時,系統會回調應用,應用需要取消短時任務。如果超時不取消,系統會終止對應的應用進程。
2.3 接口説明

表1 主要接口
以下是短時任務開發使用的主要接口,更多接口及使用方式請見後台任務管理。

接口名

描述

requestSuspendDelay(reason: string, callback: Callback): DelaySuspendInfo

申請短時任務。

getRemainingDelayTime(requestId: number): Promise

獲取對應短時任務的剩餘時間。

cancelSuspendDelay(requestId: number): void

取消短時任務。

2.4 開發步驟
  1. 導入模塊。
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
  1. 申請短時任務並實現回調。此處回調在短時任務即將結束時觸發,與應用的業務功能不耦合,短時任務申請成功後,正常執行應用本身的任務。
let id: number;         // 申請短時任務ID
let delayTime: number;  // 本次申請短時任務的剩餘時間

// 申請短時任務
function requestSuspendDelay() {
  let myReason = 'test requestSuspendDelay';   // 申請原因
  try {
    let delayInfo = backgroundTaskManager.requestSuspendDelay(myReason, () => {
    // 回調函數。應用申請的短時任務即將超時,通過此函數回調應用,執行一些清理和標註工作,並取消短時任務
      console.info('suspend delay task will timeout');
      try {
        backgroundTaskManager.cancelSuspendDelay(id);
      } catch (error) {
        console.error(`Operation cancelSuspendDelay failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
      }
    })
    id = delayInfo.requestId;
    delayTime = delayInfo.actualDelayTime;
  } catch (error) {
    console.error(`Operation requestSuspendDelay failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
  } 
}

// 執行應用本身業務
  1. 獲取短時任務剩餘時間。查詢本次短時任務的剩餘時間,用以判斷是否繼續運行其他業務,例如應用有兩個小任務,在執行完第一個小任務後,可以判斷本次短時任務是否還有剩餘時間來決定是否執行第二個小任務。
let id: number; // 申請短時任務ID

async function getRemainingDelayTime() {
  backgroundTaskManager.getRemainingDelayTime(id).then((res: number) => {
    console.info('Succeeded in getting remaining delay time.');
  }).catch((err: BusinessError) => {
    console.error(`Failed to get remaining delay time. Code: ${err.code}, message: ${err.message}`);
  })
}
  1. 取消短時任務。
let id: number; // 申請短時任務ID

function cancelSuspendDelay() {
  try {
    backgroundTaskManager.cancelSuspendDelay(id);
  } catch (error) {
    console.error(`Operation cancelSuspendDelay failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
  }
}
2.5 完整示例
申請短時任務效果圖

HarmonyOS:短時任務(ArkTS)_HarmonyOS_03


獲取短時任務剩餘時間效果圖


HarmonyOS:短時任務(ArkTS)_HarmonyOS_04


取消短時任務


HarmonyOS:短時任務(ArkTS)_鴻蒙_05

示例完整代碼

import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';

let id: number; // 申請短時任務ID
let delayTime: number; // 本次申請短時任務的剩餘時間

/**
 * 申請短時任務
 */
function requestSuspendDelay() {
  let myReason = '測試 申請短時任務'; // 申請原因
  try {
    let delayInfo = backgroundTaskManager.requestSuspendDelay(myReason, () => {
      // 回調函數。應用申請的短時任務即將超時,通過此函數回調應用,執行一些清理和標註工作,並取消短時任務
      console.info('應用申請的短時任務即將超時');
      try {
        backgroundTaskManager.cancelSuspendDelay(id);
        console.log("取消短時任務 成功 id = ", id);
      } catch (error) {
        console.error(`取消短時任務 失敗. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
      }
    })
    id = delayInfo.requestId;
    delayTime = delayInfo.actualDelayTime;
    console.log("申請短時任務 成功 id = " + id + " , delayTime = " + delayTime);

  } catch (error) {
    console.error(`申請短時任務  失敗. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
  }
}

/**
 * 獲取短時任務剩餘時間
 */
async function getRemainingDelayTime() {
  backgroundTaskManager.getRemainingDelayTime(id).then((res: number) => {
    console.info('獲取短時任務剩餘時間 成功 , res : ', res);
  }).catch((err: BusinessError) => {
    console.error(`獲取短時任務剩餘時間 失敗. Code: ${err.code}, message: ${err.message}`);
  })
}

@Entry
@Component
struct TestBackgroundTasksKit {
  @State message: string = '申請短時任務';

  build() {
    Column({ space: 30 }) {
      Button(this.message)
        .fontSize($r('app.float.page_text_font_20fp'))
        .fontWeight(FontWeight.Bold)
        .onClick(() => {
          requestSuspendDelay()
        })
        .margin({ top: 60 })

      Button("獲取短時任務剩餘時間")
        .fontSize($r('app.float.page_text_font_20fp'))
        .fontWeight(FontWeight.Bold)
        .onClick(() => {
          getRemainingDelayTime()
        })
    }
    .height('100%')
    .width('100%')
  }
}