本文同步发表于 微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

当应用退至后台(返回主界面、锁屏、应用切换)时,系统会对应用进行管控:

  • 进程挂起:应用退至后台一段时间后,进程被挂起,无法使用CPU、网络、GPS等资源

  • 进程终止:资源不足时,系统会终止部分应用进程以回收资源

Background Tasks Kit的作用

为了解决后台功能需求与系统资源管控的矛盾,HarmonyOS提供了Background Tasks Kit,支持以下四种规范化的后台任务:

任务类型 适用场景 特点
短时任务 状态保存、消息发送等实时性高、耗时短的任务 单次最长3分钟,有配额限制
长时任务 后台音乐播放、导航、设备连接等用户可感知的任务 长时间运行,避免进程挂起
延迟任务 实时性不高、可延迟执行的任务 系统统一调度,智能分配资源
代理提醒 倒计时、日历、闹钟等定时提醒 系统代理执行,进程终止后仍有效

二、短时任务

短时任务允许应用在被系统挂起前,执行一些耗时较短的操作(如状态保存、消息发送等),扩展应用在后台的运行时间。

限制与配额机制

申请时机限制
// 正确:在前台或onBackground回调内申请
onForeground() {
    this.requestShortTask();
}

onBackground() {
    this.requestShortTask(); // 在回调内申请
}

// 错误:在onStop或应用已退到后台后申请
onStop() {
    this.requestShortTask(); // 会失败
}
数量限制
  • 一个应用同一时刻最多申请3个短时任务

  • 多个短时任务可以重叠执行

配额机制
配额类型 限制值 说明
单日配额 10分钟 24小时内所有短时任务总时长
单次配额 3分钟 单次短时任务最大时长
低电量单次 1分钟 设备低电量时自动降低
剩余时间查询 支持 可查询本次任务剩余时间
配额计算规则
// 重要规则:
// 1. 仅当应用在后台时才会计时
// 2. 前台时间不计入配额
// 3. 同一时间段内的多个任务不重复计时

假设场景:

  • 应用在前台申请任务A

  • 退到后台开始计时(阶段①)

  • 回到前台(阶段②不计时)

  • 再次退到后台(阶段③计时)

  • 任务A结束后,任务B仍在运行(阶段④计时)

总耗时 = ① + ③ + ④

三、实例代码

3.1 导入模块

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

3.2 申请短时任务

let taskId: number = 0;           // 任务ID
let remainingTime: number = 0;    // 剩余时间

/**
 * 申请短时任务
 * @param reason 申请原因(用于系统日志和调试)
 */
function requestShortTask(reason: string = '状态保存') {
    try {
        // 1. 申请短时任务
        const delayInfo = backgroundTaskManager.requestSuspendDelay(
            reason,
            () => {
                // 超时回调:任务即将结束时触发
                console.warn('短时任务即将超时!请立即清理并取消任务');
                
                // 执行清理工作
                this.cleanupResources();
                
                // 必须取消任务
                this.cancelShortTask();
            }
        );
        
        // 2. 保存任务信息
        taskId = delayInfo.requestId;
        remainingTime = delayInfo.actualDelayTime;
        
        console.info(`短时任务申请成功!`);
        console.info(`任务ID: ${taskId}`);
        console.info(`获得时间: ${remainingTime}ms`);
        console.info(`最大允许: 3分钟`);
        
        // 3. 执行业务逻辑
        this.executeBackgroundWork();
        
    } catch (error) {
        const err = error as BusinessError;
        console.error(`短时任务申请失败!错误码: ${err.code}, 消息: ${err.message}`);
        
        // 常见错误码:
        // 12900001: 配额已用完
        // 12900002: 申请时机不正确
        // 12900003: 已达到最大任务数
    }
}

3.3 查询剩余时间

/**
 * 查询短时任务剩余时间
 * 用于判断是否继续执行其他任务
 */
async function checkRemainingTime(): Promise<boolean> {
    try {
        const remaining = await backgroundTaskManager.getRemainingDelayTime(taskId);
        console.info(`剩余时间: ${remaining}ms`);
        
        // 判断是否还有足够时间执行新任务
        if (remaining > 30000) { // 30秒
            console.info('时间充足,可以继续执行其他任务');
            return true;
        } else {
            console.warn('时间不足,建议只执行关键任务');
            return false;
        }
        
    } catch (error) {
        const err = error as BusinessError;
        console.error(`查询剩余时间失败!错误码: ${err.code}, 消息: ${err.message}`);
        return false;
    }
}

// 使用示例
async function executeMultipleTasks() {
    // 执行第一个任务
    await this.saveAppState();
    
    // 检查剩余时间
    const hasTime = await this.checkRemainingTime();
    
    // 根据时间决定是否执行第二个任务
    if (hasTime) {
        await this.sendPendingMessages();
    } else {
        console.warn('时间不足,跳过非关键任务');
    }
}

3.4 取消短时任务

/**
 * 取消短时任务
 * 重要:任务完成后必须主动取消!
 */
function cancelShortTask() {
    if (taskId === 0) {
        console.warn('没有活动的短时任务');
        return;
    }
    
    try {
        backgroundTaskManager.cancelSuspendDelay(taskId);
        console.info('短时任务已取消');
        
        // 重置状态
        taskId = 0;
        remainingTime = 0;
        
    } catch (error) {
        const err = error as BusinessError;
        console.error(`取消短时任务失败!错误码: ${err.code}, 消息: ${err.message}`);
    }
}

3.5 完整代码

@Entry
@Component
struct BackgroundTaskDemo {
    private taskId: number = 0;
    
    // 应用进入后台
    onBackground() {
        console.info('应用进入后台,申请短时任务');
        
        // 申请短时任务进行状态保存
        this.requestShortTask('保存用户数据');
    }
    
    // 申请短时任务
    private requestShortTask(reason: string) {
        try {
            const delayInfo = backgroundTaskManager.requestSuspendDelay(
                reason,
                this.onTaskTimeout.bind(this)
            );
            
            this.taskId = delayInfo.requestId;
            
            // 执行后台任务
            this.saveUserData();
            
        } catch (error) {
            console.error(`申请失败: ${error.message}`);
        }
    }
    
    // 超时回调
    private onTaskTimeout() {
        console.warn('任务即将超时,立即保存进度并取消');
        
        // 保存当前进度
        this.saveProgress();
        
        // 取消任务
        this.cancelTask();
    }
    
    // 保存用户数据
    private async saveUserData() {
        try {
            // 模拟数据保存
            await this.saveToStorage();
            console.info('数据保存完成');
            
            // 任务完成,立即取消
            this.cancelTask();
            
        } catch (error) {
            console.error('保存失败', error);
        }
    }
    
    // 取消任务
    private cancelTask() {
        if (this.taskId > 0) {
            backgroundTaskManager.cancelSuspendDelay(this.taskId);
            this.taskId = 0;
        }
    }
}

四、注意事项

4.1 必须遵守的规则

1. 及时取消任务
// 正确:任务完成立即取消
async function saveData() {
    await data.save();
    backgroundTaskManager.cancelSuspendDelay(taskId);
}
2. 正确处理超时
// 超时回调中必须:
// 1. 执行紧急清理
// 2. 保存当前状态
// 3. 取消任务
const timeoutCallback = () => {
    // 紧急保存
    this.emergencySave();
    
    // 记录日志
    console.warn('任务超时,已保存进度');
    
    // 必须取消!
    this.cancelTask();
};
3. 配额管理
class ShortTaskManager {
    private usedQuotaToday: number = 0;
    private MAX_DAILY_QUOTA: number = 10 * 60 * 1000; // 10分钟
    
    // 申请前检查配额
    canRequestTask(duration: number): boolean {
        if (this.usedQuotaToday + duration > this.MAX_DAILY_QUOTA) {
            console.warn('今日配额不足');
            return false;
        }
        
        // 检查设备电量
        if (this.isLowBattery()) {
            console.warn('低电量模式,任务时间可能缩短');
        }
        
        return true;
    }
    
    // 记录已用配额
    recordQuotaUsage(duration: number) {
        this.usedQuotaToday += duration;
        console.info(`今日已用配额: ${this.usedQuotaToday}ms`);
    }
}

六、短时任务 vs 其他后台任务

场景特征 推荐方案 理由
任务时长 < 3分钟
实时性要求高
短时任务 立即执行,不延迟
任务时长 > 3分钟
用户可感知
长时任务 避免进程挂起
可延迟执行
不紧急
延迟任务 系统智能调度,更省电
定时提醒
进程终止后仍需执行
代理提醒 系统代理,可靠性高

备注

  1. 申请时机:必须在前台onBackground回调中申请

  2. 配额要管理:单日10分钟,单次3分钟,低电量时1分钟

  3. 任务要取消:完成或超时必须调用cancelSuspendDelay(),如果超时不取消,系统会对应用进行管控,包括进程挂起和进程终止。

  4. 时间要检查:使用getRemainingDelayTime()合理安排任务

  5. 数量要控制:同一时刻不超过3个任务

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐