未验证 提交 d396841d 编写于 作者: O openharmony_ci 提交者: Gitee

!21224 精品内容上传

Merge pull request !21224 from ningning/master
# 后台任务(Background Task)管理
- 后台任务
- [后台任务概述](background-task-overview.md)
- [短时任务开发指导](transient-task-dev-guide.md)
- [长时任务开发指导](continuous-task-dev-guide.md)
- [延迟任务调度开发指导](work-scheduler-dev-guide.md)
- [延迟任务回调能力开发指导(WorkSchedulerExtensionAbility)](workscheduler-extensionability.md)
- [申请能效资源开发指导](efficiency-resources-apply-dev-guide.md)
- 后台代理提醒
- [后台代理提醒概述](reminder-agent-overview.md)
- [后台代理提醒开发指导](reminder-agent-development.md)
- [后台任务总体概述](background-task-overview.md)
- [短时任务](transient-task.md)
- [长时任务](continuous-task.md)
- [延迟任务](work-scheduler.md)
- [代理提醒](agent-powered-reminder.md)
- [能效资源申请(仅对系统特权应用开放)](efficiency-resource-request.md)
\ No newline at end of file
# 后台代理提醒开发指导
# 代理提醒
## 概述
## 接口说明
### 功能介绍
应用退到后台或进程终止后,仍然有一些提醒用户的定时类任务,例如购物类应用抢购提醒等,为满足此类功能场景,系统提供了代理提醒(reminderAgentManager)的能力。当应用退至后台或进程终止后,系统会代理应用做相应的提醒。当前支持的提醒类型包括:倒计时、日历和闹钟。
- 倒计时类:基于倒计时的提醒功能。
- 日历类:基于日历的提醒功能。
- 闹钟类:基于时钟的提醒功能。
### 约束与限制
- **个数限制**:一个三方应用支持最多30个有效提醒(有效即发布成功),一个系统应用支持最多10000个有效提醒,整个系统最多支持12000个有效提醒。
后台代理提醒功能主要提供后台提醒通知发布接口,开发者可调用这些接口创建定时提醒,包括倒计时、日历、闹钟三种提醒类型。[reminderAgentManager](../reference/apis/js-apis-reminderAgentManager.md)封装了发布、取消提醒通知的方法
- **跳转限制**:点击提醒通知后跳转的应用必须是申请代理提醒的本应用
**表1** reminderAgentManager主要接口
| 接口名 | 描述 |
| ---------------------------------------- | ---------------------------------------- |
| publishReminder(reminderReq:&nbsp;ReminderRequest,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void<br/>publishReminder(reminderReq:&nbsp;ReminderRequest):&nbsp;Promise&lt;number&gt; | 发布一个定时提醒类通知。<br/>-&nbsp;单个应用有效的提醒个数最多支持30个(不包括已经超时,即后续不会再提醒的提醒实例)。<br/>-&nbsp;整个系统有效的提醒个数最多支持2000个(不包括已经超时,即后续不会再提醒的提醒实例)。 |
| cancelReminder(reminderId:&nbsp;number,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void<br/>cancelReminder(reminderId:&nbsp;number):&nbsp;Promise&lt;void&gt; | 取消一个指定的提醒类通知(reminderId从publishReminder的返回值获取)。 |
| getValidReminders(callback:&nbsp;AsyncCallback&lt;Array&lt;ReminderRequest&gt;&gt;):&nbsp;void<br/>getValidReminders():&nbsp;Promise&lt;Array&lt;ReminderRequest&gt;&gt; | 获取当前应用设置的所有有效的提醒。 |
| cancelAllReminders(callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void<br/>cancelAllReminders():&nbsp;Promise&lt;void&gt; | 取消当前应用设置的所有提醒。 |
| addNotificationSlot(slot:&nbsp;NotificationSlot,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void<br/>addNotificationSlot(slot:&nbsp;NotificationSlot):&nbsp;Promise&lt;void&gt; | 注册一个提醒类需要使用的NotificationSlot。 |
| removeNotificationSlot(slotType:&nbsp;notification.SlotType,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void<br/>removeNotificationSlot(slotType:&nbsp;notification.SlotType):&nbsp;Promise&lt;void&gt; | 删除指定类型的NotificationSlot。 |
## 接口说明
**表1** 主要接口
以下是代理提醒的相关接口,下表均以Promise形式为例,更多接口及使用方式请见后台代理提醒文档。
| 接口名 | 描述 |
| -------- | -------- |
| publishReminder(reminderReq: ReminderRequest): Promise&lt;number&gt; | 发布一个定时提醒类通知 |
| cancelReminder(reminderId: number): Promise&lt;void&gt; | 取消一个指定的提醒类通知 |
| getValidReminders(): Promise&lt;Array&lt;ReminderRequest&gt;&gt; | 获取当前应用设置的所有有效的提醒 |
| cancelAllReminders(): Promise&lt;void&gt; | 取消当前应用设置的所有提醒 |
| addNotificationSlot(slot: NotificationSlot): Promise&lt;void&gt; | 注册一个提醒类需要使用的通知通道(NotificationSlot) |
| removeNotificationSlot(slotType: notification.SlotType): Promise&lt;void&gt; | 删除指定的通知通道(NotificationSlot) |
## 开发步骤
1. 需要申请`ohos.permission.PUBLISH_AGENT_REMINDER`权限,配置方式请参见[配置文件权限声明](../security/accesstoken-guidelines.md#配置文件权限声明)
1. 申请ohos.permission.PUBLISH_AGENT_REMINDER权限,配置方式请参阅[配置文件权限声明](../security/accesstoken-guidelines.md#配置文件权限声明)
2. [使能通知开关](../notification/notification-enable.md)获得用户授权后,才能使用代理提醒功能。
2. [使能通知开关](../notification/notification-enable.md)获得用户授权后,才能使用代理提醒功能。
3. 导入模块。
```js
import reminderAgentManager from '@ohos.reminderAgentManager';
import notificationManager from '@ohos.notificationManager';
```
4. 定义目标提醒代理。开发者根据实际需要,选择定义如下类型的提醒。
- 定义倒计时实例。
- 定义倒计时实例。
```js
let targetReminderAgent: reminderAgentManager.ReminderRequestTimer = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_TIMER, // 提醒类型为倒计时类型
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_TIMER, // 提醒类型为倒计时类型
triggerTimeInSeconds: 10,
actionButton: [ // 设置弹出的提醒通知信息上显示的按钮类型和标题
{
......@@ -43,11 +61,11 @@
type: reminderAgentManager.ActionButtonType.ACTION_BUTTON_TYPE_CLOSE
}
],
wantAgent: { // 点击提醒通知后跳转的目标UIAbility信息
wantAgent: { // 点击提醒通知后跳转的目标UIAbility信息
pkgName: 'com.example.myapplication',
abilityName: 'EntryAbility'
},
maxScreenWantAgent: { // 全屏显示提醒到达时自动拉起的目标Ability信息
maxScreenWantAgent: { // 全屏显示提醒到达时自动拉起的目标UIAbility信息
pkgName: 'com.example.myapplication',
abilityName: 'EntryAbility'
},
......@@ -58,15 +76,16 @@
slotType: notificationManager.SlotType.SOCIAL_COMMUNICATION // 指明提醒的Slot类型
}
```
- 定义日历实例。
- 定义日历实例。
```js
let targetReminderAgent: reminderAgentManager.ReminderRequestCalendar = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_CALENDAR, // 提醒类型为日历类型
dateTime: { // 指明提醒的目标时间
dateTime: { // 指明提醒的目标时间
year: 2023,
month: 7,
day: 30,
month: 1,
day: 1,
hour: 11,
minute: 14,
second: 30
......@@ -87,13 +106,13 @@
pkgName: 'com.example.myapplication',
abilityName: 'EntryAbility'
},
maxScreenWantAgent: { // 点击提醒通知后跳转的目标UIAbility信息
maxScreenWantAgent: { // 全屏显示提醒到达时自动拉起的目标UIAbility信息
pkgName: 'com.example.myapplication',
abilityName: 'EntryAbility'
},
ringDuration: 5, // 指明响铃时长(单位:秒)
snoozeTimes: 2, // 指明延迟提醒次数
timeInterval: 300, // 执行延迟提醒间隔(单位:秒)
timeInterval: 5, // 执行延迟提醒间隔(单位:秒)
title: 'this is title', // 指明提醒标题
content: 'this is content', // 指明提醒内容
expiredContent: 'this reminder has expired', // 指明提醒过期后需要显示的内容
......@@ -102,8 +121,9 @@
slotType: notificationManager.SlotType.SOCIAL_COMMUNICATION // 指明提醒的Slot类型
}
```
- 定义闹钟实例。
- 定义闹钟实例。
```js
let targetReminderAgent: reminderAgentManager.ReminderRequestAlarm = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_ALARM, // 提醒类型为闹钟类型
......@@ -124,13 +144,13 @@
pkgName: 'com.example.myapplication',
abilityName: 'EntryAbility'
},
maxScreenWantAgent: { // 点击提醒通知后跳转的目标UIAbility信息
maxScreenWantAgent: { // 全屏显示提醒到达时自动拉起的目标UIAbility信息
pkgName: 'com.example.myapplication',
abilityName: 'EntryAbility'
},
ringDuration: 5, // 指明响铃时长(单位:秒)
snoozeTimes: 2, // 指明延迟提醒次数
timeInterval: 300, // 执行延迟提醒间隔(单位:秒)
timeInterval: 5, // 执行延迟提醒间隔(单位:秒)
title: 'this is title', // 指明提醒标题
content: 'this is content', // 指明提醒内容
expiredContent: 'this reminder has expired', // 指明提醒过期后需要显示的内容
......@@ -141,43 +161,37 @@
```
5. 发布相应的提醒代理。代理发布后,应用即可使用后台代理提醒功能。
```js
try {
reminderAgentManager.publishReminder(targetReminderAgent).then(res => {
console.info('publishReminder promise reminderId: ' + res);
let reminderId: number = res;
// ...
console.info('Succeeded in publishing reminder. ');
let reminderId: number = res; // 发布的提醒ID
}).catch(err => {
console.info('publishReminder err code: ' + err.code + ' message:' + err.message);
console.error(`Failed to publish reminder. Code: ${err.code}, message: ${err.message}`);
})
} catch (error) {
console.info('publishReminder code: ' + error.code + ' message:' + error.message);
} catch (err) {
console.error(`Failed to publish reminder. Code: ${err.code}, message: ${err.message}`);
}
```
以闹钟为例,运行效果如下图所示。
![zh-cn_image_0000001416585578](figures/zh-cn_image_0000001416585578.png)
6. 若需要删除提醒任务,可以通过调用[reminderAgentManager.cancelReminder()](../reference/apis/js-apis-reminderAgentManager.md#reminderagentmanagercancelreminder)方法来实现。
```js
let reminderId = 0; // reminderId的值从发布提醒代理成功之后的回调中获得
6. 根据需要删除提醒任务。
```js
try {
// reminderId的值从发布提醒代理成功之后的回调中获得
reminderAgentManager.cancelReminder(reminderId).then(() => {
console.log("cancelReminder promise");
console.log('Succeeded in canceling reminder.');
}).catch(err => {
console.log("promise err code: " + err.code + ", message:" + err.message);
console.error(`Failed to cancel reminder. Code: ${err.code}, message: ${err.message}`);
});
} catch (error) {
console.log("cancelReminder code: " + error.code + ", message: " + error.message);
};
} catch (err) {
console.error(`Failed to cancel reminder. Code: ${err.code}, message: ${err.message}`);
}
```
## 相关实例
基于后台代理提醒的开发,有以下相关实例可供参考:
基于代理提醒,有以下相关实例可供参考:
- [闹钟(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/CommonEventAndNotification/AlarmClock)
- [闹钟(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/CommonEventAndNotification/AlarmClock)
\ No newline at end of file
# 后台任务概述
# 后台任务总体概述
后台应用频繁活动,会造成用户设备耗电快、卡顿等现象。因此,为了支撑性能、功耗诉求,系统仅允许应用在后台执行规范内的活动,规范外的活动默认会被挂起,当资源不足时会被回收。
针对应用或业务模块处于后台(无可见界面)时,有需要继续执行或者后续执行的业务,可基于业务类型,申请[短时任务](#短时任务)延迟挂起或者[长时任务](#长时任务)避免进入挂起状态;使用[延迟调度任务](#延迟任务),执行对实时性要求不高的任务;同时针对特权应用,如果需要更加灵活的配置,可以申请[能效资源](#申请能效资源)
**资源使用约束** :对于运行的业务,系统会给予一定的资源配额约束,包括进程在连续一段时间内内存的使用、CPU使用占比,以及24小时磁盘写的IO量,均有对应的配额上限。超过配额上限时,如果进程处于前台,系统会有对应的warning日志,如果进程处于后台,系统会查询并终止该进程。
## 功能介绍
设备返回主界面、锁屏、应用切换等操作会使应用退至后台。应用退至后台后,如果继续活动,可能会造成设备耗电快、用户界面卡顿等现象。为了降低设备耗电速度、保障用户使用流畅度,系统会对退至后台的应用进行管控,包括进程挂起(即系统不再为应用进程分配CPU资源,同时对应的[公共事件](../application-models/common-event-overview.md)等不再发给应用进程)和进程终止。
## 后台任务类型
OpenHarmony将后台任务分为四种类型,并提供了一个资源申请的扩展功能:
**无后台业务** :应用或业务模块退到后台后,无任务需要处理。
**短时任务** :应用或业务模块退到后台后,如果有紧急不可推迟且短时间能完成的任务,如应用退后台要进行数据压缩,不可中断,则使用短时任务申请延迟进入挂起(Suspend)状态。
**长时任务** :如果是用户发起的可感知业务需要长时间后台运行,如后台播放音乐、导航、设备连接、VoIP等,则使用长时任务避免进入挂起(Suspend)状态。
**延迟任务** :延迟任务调度给应用提供一个机制,允许应用根据系统安排,在系统空闲时执行实时性不高的任务。当满足设定条件的时候,任务会被放入待调度队列,当系统空闲时调度该任务。
**能效资源** :能效资源包括CPU资源、WORK_SCHEDULER资源、软件资源(COMMON_EVENT, TIMER)、硬件资源(GPS, BLUETOOTH)。如果应用或者进程申请了能效资源,那么根据能效资源的类型会拥有相应的特权,例如申请了CPU资源的可以不被挂起,申请了WORK_SCHEDULER后延时任务可以拥有更长的执行时间。
## 最佳后台任务选择
![后台任务选择](figures/bgtask_choice.png)
## 短时任务
退到后台的应用有不可中断且短时间能完成的任务时,可以使用短时任务机制。该机制允许应用在后台短时间内完成任务,保障应用业务运行不受后台生命周期管理的影响。
> **说明:**
>
> 短时任务仅针对应用的临时任务提供资源使用生命周期保障,限制单次最大使用时长为3分钟,全天使用配额默认为10分钟(具体时长系统根据应用场景和系统状态智能调整)。
### 短时任务使用约束
短时任务的使用需要遵从如下约束和规则:
- **申请时机**:允许应用在前台时,或退后台在被挂起之前(应用退到后台默认有6~12秒的运行时长,具体时长由系统根据具体场景决定)申请延迟挂起,否则可能被挂起(Suspend),导致申请失败。
- **超时**:延迟挂起即将超时(Timeout),系统通过回调知会应用,应用需要取消对应的延迟挂起。如果超时不取消,该应用会被强制终止。
- 应用退至后台一小段时间(由系统定义),应用进程会被挂起。
- **取消时机**:任务完成后,应用应主动取消延迟挂起,不要等到系统回调后再取消,否则会影响该应用的后台允许运行时长配额
- 应用退至后台,在后台被访问一小段时间(由系统定义)后,应用进程会被挂起
- **配额机制**:为了防止应用滥用保活,或者申请后不取消,每个应用每天都会有一定配额(会根据用户的使用习惯动态调整),其中单日配额默认为10分钟,单次配额最大为3分钟。配额消耗完就不再允许申请短时任务,所以应用完成短时任务后应立刻取消延迟挂起,避免消耗配额。(注:该配额指的是申请的时长,系统默认应用在后台运行的时间不计算在内)。
- 资源不足时,系统会终止部分应用进程(即回收该进程的所有资源)。
## 长时任务
同时,为了保障后台音乐播放、日历提醒等功能的正常使用,系统提供了规范内受约束的后台任务,扩展应用在后台运行时间。
长时任务给用户能够直观感受到的且需要一直在后台运行的业务提供后台运行生命周期的保障。比如:业务需要在后台播放声音、需要在后台持续导航定位等。此类用户可以直观感知到的后台业务行为,可以通过使用长时任务对应的后台模式保障业务在后台的运行,支撑应用完成在后台的业务。
### 后台模式分类
OpenHarmony提供了九种后台模式,供需要在后台做长时任务的业务使用,具体的后台模式类型如下:
**表1** 长时任务种类
| 后台模式 | 说明 | 通知栏显示提示 | 备注 |
| --------------------- | ------------------------- | ------------ | ------------------------- |
| dataTransfer | 通过网络/对端设备进行数据下载、备份、分享、传输等 | 正在运行数据传输任务 | - |
| audioPlayback | 音频输出 | 正在运行音频播放任务 | - |
| audioRecording | 音频输入 | 正在运行录音任务 | - |
| location | 定位、导航 | 正在运行定位任务 | - |
| bluetoothInteraction | 蓝牙传输 | 正在运行蓝牙相关任务 | - |
| multiDeviceConnection | 应用跨设备多端协同 | 正在运行分布式任务 | - |
| wifiInteraction | WLAN传输 | 正在运行WLAN相关任务 | System API,仅对System权限应用开放 |
| voip | 音视频电话、VOIP | 正在运行通话相关任务 | System API,仅对System权限应用开放 |
| taskKeeping | 计算任务 | 正在运行计算任务 | 仅在特定设备生效 |
### 长时任务使用约束
- 如果用户选择可感知业务(如播音、导航等),触发对应后台模式,在任务启动时,系统会强制弹出通知提醒用户。
- 如果任务结束,应用应主动退出后台模式。若在后台运行期间,系统检测到应用并未使用对应后台模式的资源,则会被挂起(Suspend)。
- 避免不合理地申请后台长时任务,长时任务类型要与应用的业务类型匹配。如果执行的任务和申请的类型不匹配,也会被系统检测到并被挂起(Suspend)。
- 长时任务是为了真正在后台长时间执行某个任务,如果一个应用申请了长时任务,但在实际运行过程中,并未真正运行或执行此类任务时,也会被系统检测到并被挂起(Suspend)。
- 一个Ability同一时刻只能申请运行一个长时任务。如果同一时刻需要申请多个长时任务,需要创建多个Ability,每个Ability申请一个长时任务。
## 延迟任务
延迟任务调度给应用提供一个机制,允许应用根据系统安排,在系统空闲时执行实时性要求不高的任务,比如设备空闲时候做一次数据学习等场景。当应用申请延迟任务的时候,任务会被放入待调度队列,系统会根据当前状态,如内存、功耗、温度等统一决策最优的调度时机。同时支持任务的持久化,应用退出或者设备重启,设置的任务同样能够被触发。
### 延迟任务调度约束
延迟调度任务的使用需要遵从如下约束和规则:
- **超时**:系统会设置超时机制,延迟任务回调只允许运行一段时间,超时之后,系统会主动停止。默认的超时限制为2分钟,对于系统应用,可以通过[申请能效资源](efficiency-resources-apply-dev-guide.md)获取更长的执行时间(充电状态20分钟,非充电状态10分钟)。
- **执行频率**:系统会根据应用的活跃度对延迟任务做分级管控,限制延迟任务调度的执行频率。对于通过能效资源接口申请了WORK_SCHEDULER资源的应用,在资源的有效期内,它的延迟任务执行频率不受限制。
| 应用分组 | 延迟任务执行频率约束 |
| ---------------------------------------- | ---------- |
| 活跃 | 最小间隔2小时 |
| 每日使用 | 最小间隔4小时 |
| 经常使用 | 最小间隔24小时 |
| 不经常使用 | 最小间隔48小时 |
| 受限分组 | 禁止 |
| 未使用分组 | 禁止 |
| [能效资源豁免分组](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#resourcetype) | 执行频率不受限制 |
- **WorkInfo设置参数约束**
- workId、bundleName、abilityName为必填项,bundleName必须填本应用,否则校验失败。
- 至少要设置一个满足的条件。
## 后台任务类型
- 重复任务时间间隔至少20分钟,当设置重复任务时间间隔时,必须设置始终重复和重复次数中的一个
OpenHarmony标准系统支持规范内受约束的后台任务,包括短时任务、长时任务、延迟任务、代理提醒和能效资源
- 携带参数信息支持number、string、bool三种类型
开发者可以根据如下的功能介绍,选择合适的后台任务,以满足应用退至后台后继续运行的需求
## 申请能效资源
- **短时任务**:适用于实时性要求高、耗时不长的任务,例如状态保存。
供系统应用使用的能效资源可以分为两类:软件资源(WORK_SCHEDULER, COMMON_EVENT, TIMER),硬件资源(CPU, GPS, BLUETOOTH, AUDIO)
- **长时任务**:适用于长时间运行在后台、用户可感知的任务,例如后台播放音乐、导航、设备连接等,使用长时任务避免应用进程被挂起
应用申请不同的能效资源后可以执行相应的操作:
* 申请CPU资源后可以不被挂起,直到任务完成。
* 申请WORK_SCHEDULER资源后不受延迟任务执行频率约束,且任务执行时间增加。
* 申请COMMON_EVENT资源后,应用在后台处于挂起状态时,仍然能够接收到系统公共事件。
* 申请TIMER资源后,应用能够使用定时器执行精确定时任务。
* 申请资源(GPS, BLUETOOTH, AUDIO)后,应用在后台被挂起后,依然能够被管理相关硬件的服务唤醒,执行相应的任务。
- **延迟任务**:对于实时性要求不高、可延迟执行的任务,系统提供了延迟任务,即满足条件的应用退至后台后被放入执行队列,系统会根据内存、功耗等统一调度。
- **代理提醒**:代理提醒是指应用退后台或进程终止后,系统会代理应用做相应的提醒。适用于定时提醒类业务,当前支持的提醒类型包括倒计时、日历和闹钟三类。
**表2** 能效资源种类
同时,对于提供基础能力的系统应用,系统单独提供[能效资源申请](efficiency-resource-request.md)接口。应用调用能效资源接口后,系统对应用进行一定的管控豁免。
| 参数名 | 参数值 | 描述 |
| -------------- | ---- | ------------------- |
| CPU | 1 | CPU资源,申请后不被挂起 |
| COMMON_EVENT | 2 | 公共事件,申请后挂起状态下不被代理掉 |
| TIMER | 4 | 计时器,申请后挂起状态下不被代理掉 |
| WORK_SCHEDULER | 8 | 延迟任务,申请后有更长的执行时间 |
| BLUETOOTH | 16 | 蓝牙相关,申请后挂起状态下不被代理掉 |
| GPS | 32 | GPS相关,申请后挂起状态下不被代理掉 |
| AUDIO | 64 | 音频资源,申请后挂起状态下不被代理掉 |
**图1** 后台任务类型选择  
![bgtask_choice](figures/bgtask_choice.png)
### 能效资源使用约束
- 能效资源申请或者释放可以由进程或者应用发起,由应用发起的资源释放会释放属于它的同类型的所有资源,包括进程申请的资源。例如应用申请了CPU资源,进程申请了CPU和WORK_SCHEDULER资源,当应用释放CPU资源的时候,会将进程的CPU资源一同释放,同时不同类型的WORK_SCHEDULER资源不受影响。由进程发起的资源释放对应用申请的资源没有影响,例如当应用和进程同时申请了CPU,进程发起了CPU资源释放,应用的CPU资源不会被释放。
- 同时申请同一类持久资源和非持久资源,持久资源会覆盖非持久资源,在超时时不会释放资源。例如应用首先申请了10s的CPU资源,然后在第5s的时候申请了持久的CPU资源,那么资源会变成持久的,非持久的CPU资源记录会被持久化的CPU资源记录覆盖,到了第10s的时候资源不会被释放,如果在第8s的时候提前释放了资源,那么会将CPU资源释放,无法单独释放其中非持久的或者持久的CPU资源。
- WORK_SCHEDULER资源只能由应用申请和释放,不能由进程申请和释放。
- 需要使用能效资源的应用必须是系统应用,同时需要向应用中心提出申请,配置相应的特权。
> **说明:**
>
> 1. 系统仅支持规范内受约束的后台任务。应用退至后台后,若未使用规范内的后台任务或选择的后台任务类型不正确,对应的应用进程会被挂起或终止。
>
> 2. 应用申请了规范内的后台任务,仅会提升应用进程被回收的优先级。当系统资源严重不足时,即使应用进程申请了规范内的后台任务,系统仍会终止部分进程,用以保障系统稳定性。
\ No newline at end of file
# 长时任务开发指导
## 场景说明
如果应用需要在后台长时间执行用户可感知的任务,如后台播放音乐、导航、设备连接、VoIP等,则使用长时任务避免进入挂起(Suspend)状态。
长时任务在后台执行没有时间限制。为了避免该机制被滥用,系统只允许申请有限个数的长时任务类型,同时会有相应的通知提示与长时任务相关联,使用户可感知,并且系统会添加相应的校验机制,确保应用是的确在执行相应的长时任务。
## 接口说明
**表1** 长时任务主要接口
| 接口名 | 描述 |
| ---------------------------------------- | ---------------------------- |
| startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent): Promise&lt;void&gt; | 服务启动后,向系统申请长时任务,使服务一直保持后台运行。 |
| stopBackgroundRunning(context: Context): Promise&lt;void&gt; | 停止后台长时任务的运行。 |
其中,wantAgent的信息详见([WantAgent](../reference/apis/js-apis-app-ability-wantAgent.md)
**表2** 后台模式类型
| 参数名 | 描述 | 配置项 |
| ----------------------- | -------------- | --------------------- |
| DATA_TRANSFER | 数据传输 | dataTransfer |
| AUDIO_PLAYBACK | 音频播放 | audioPlayback |
| AUDIO_RECORDING | 录音 | audioRecording |
| LOCATION | 定位导航 | location |
| BLUETOOTH_INTERACTION | 蓝牙相关 | bluetoothInteraction |
| MULTI_DEVICE_CONNECTION | 多设备互联 | multiDeviceConnection |
| WIFI_INTERACTION | WLAN相关(系统保留) | wifiInteraction |
| VOIP | 音视频通话(系统保留) | voip |
| TASK_KEEPING | 计算任务(仅供特定设备使用) | taskKeeping |
## 开发步骤
### 基于Stage模型
Stage模型的相关信息参考[Stage模型开发概述](../application-models/stage-model-development-overview.md)
1、在module.json5文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING、同时为需要使用长时任务的ability声明相应的后台模式类型。
```
"module": {
"abilities": [
{
"backgroundModes": [
"dataTransfer",
"location"
], // 后台模式类型
}
],
"requestPermissions": [
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING" // 长时任务权限
}
]
}
```
2、在应用内执行长时任务时,由于元能力启动管控规则限制,不支持同应用通过startAbilityByCall的形式在后台创建并运行Ability。可以直接在page中,执行相应的代码。Stage模型的Ability使用参考[Stage模型开发指导-UIAbility组件](../application-models/uiability-overview.md)
```ts
import wantAgent from '@ohos.app.ability.wantAgent';
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
@Entry
@Component
struct Index {
@State message: string = 'test'
// 通过getContext方法,来获取page所在的Ability上下文。
private context: any = getContext(this)
startContinuousTask() {
let wantAgentInfo = {
// 点击通知后,将要执行的动作列表
wants: [
{
bundleName: "com.example.myapplication",
abilityName: "EntryAbility",
}
],
// 点击通知后,动作类型
operationType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
// 通过wantAgent模块的getWantAgent方法获取WantAgent对象
try {
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
try {
backgroundTaskManager.startBackgroundRunning(this.context,
backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
console.info("Operation startBackgroundRunning succeeded");
}).catch((err) => {
console.error("Operation startBackgroundRunning failed Cause: " + err);
});
} catch (error) {
console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
}
});
} catch (error) {
console.error(`Operation getWantAgent failed. code is ${error.code} message is ${error.message}`);
}
}
stopContinuousTask() {
try {
backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
console.info("Operation stopBackgroundRunning succeeded");
}).catch((err) => {
console.error("Operation stopBackgroundRunning failed Cause: " + err);
});
} catch (error) {
console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
}
}
build() {
Row() {
Column() {
Text("Index")
.fontSize(50)
.fontWeight(FontWeight.Bold)
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();
})
}
.width('100%')
}
.height('100%')
}
}
```
3、当需要跨设备或者跨应用在后台执行长时任务时,可以通过Call的方式在后台创建并运行Ability。使用方式参考[Call调用开发指南(同设备)](../application-models/uiability-intra-device-interaction.md#通过call调用实现uiability交互仅对系统应用开放)[Call调用开发指南(跨设备)](../application-models/hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import wantAgent from '@ohos.app.ability.wantAgent';
const MSG_SEND_METHOD: string = 'CallSendMsg';
let mContext = null;
function startContinuousTask() {
let wantAgentInfo = {
// 点击通知后,将要执行的动作列表
wants: [
{
bundleName: "com.example.myapplication",
abilityName: "EntryAbility",
}
],
// 点击通知后,动作类型
operationType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
// 通过wantAgent模块的getWantAgent方法获取WantAgent对象
try {
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
try {
backgroundTaskManager.startBackgroundRunning(mContext,
backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
console.info("Operation startBackgroundRunning succeeded");
}).catch((error) => {
console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
});
} catch (error) {
console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
}
});
} catch (error) {
console.error(`Operation getWantAgent failed. code is ${error.code} message is ${error.message}`);
}
}
function stopContinuousTask() {
try {
backgroundTaskManager.stopBackgroundRunning(mContext).then(() => {
console.info("Operation stopBackgroundRunning succeeded");
}).catch((error) => {
console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
});
} catch (error) {
console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
}
}
class MyParcelable {
num: number = 0;
str: String = "";
constructor(num, string) {
this.num = num;
this.str = string;
}
marshalling(messageSequence) {
messageSequence.writeInt(this.num);
messageSequence.writeString(this.str);
return true;
}
unmarshalling(messageSequence) {
this.num = messageSequence.readInt();
this.str = messageSequence.readString();
return true;
}
}
function sendMsgCallback(data) {
console.info('BgTaskAbility funcCallBack is called ' + data)
let receivedData = new MyParcelable(0, "")
data.readParcelable(receivedData)
console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`)
// 可以根据Caller端发送的序列化数据的str值,执行不同的方法。
if (receivedData.str === 'start_bgtask') {
startContinuousTask()
} else if (receivedData.str === 'stop_bgtask') {
stopContinuousTask();
}
return new MyParcelable(10, "Callee test");
}
export default class BgTaskAbility extends UIAbility {
onCreate(want, launchParam) {
console.info("[Demo] BgTaskAbility onCreate")
this.callee.on("test", sendMsgCallback);
try {
this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
} catch (error) {
console.error(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
}
mContext = this.context;
}
onDestroy() {
console.info("[Demo] BgTaskAbility onDestroy")
}
onWindowStageCreate(windowStage) {
console.info("[Demo] BgTaskAbility onWindowStageCreate")
windowStage.loadContent("pages/index").then((data)=> {
console.info(`load content succeed with data ${JSON.stringify(data)}`)
}).catch((error)=>{
console.error(`load content failed with error ${JSON.stringify(error)}`)
})
}
onWindowStageDestroy() {
console.info("[Demo] BgTaskAbility onWindowStageDestroy")
}
onForeground() {
console.info("[Demo] BgTaskAbility onForeground")
}
onBackground() {
console.info("[Demo] BgTaskAbility onBackground")
}
};
```
### 基于FA模型
基于FA的Service Ability使用,参考[ServiceAbility开发指导](../application-models/serviceability-overview.md)
当不需要与后台执行的长时任务交互时,可以采用startAbility()方法启动Service Ability。并在Service Ability的onStart回调方法中,调用长时任务的申请接口,声明此服务需要在后台长时运行。当任务执行完,再调用长时任务取消接口,及时释放资源。
当需要与后台执行的长时任务交互时(如播放音乐等)。可以采用connectAbility()方法启动并连接Service Ability。在获取到服务的代理对象后,与服务进行通信,控制长时任务的申请和取消。
1、在config.json文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING、同时为需要使用长时任务的Service Ability声明相应的后台模式类型。
```json
"module": {
"package": "com.example.myapplication",
"abilities": [
{
"backgroundModes": [
"dataTransfer",
"location"
], // 后台模式类型
"type": "service" // ability类型为service
}
],
"reqPermissions": [
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING" // 长时任务权限
}
]
}
```
2、在Service Ability调用长时任务的申请和取消接口。
```js
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import featureAbility from '@ohos.ability.featureAbility';
import wantAgent from '@ohos.app.ability.wantAgent';
import rpc from "@ohos.rpc";
function startContinuousTask() {
let wantAgentInfo = {
// 点击通知后,将要执行的动作列表
wants: [
{
bundleName: "com.example.myapplication",
abilityName: "EntryAbility"
}
],
// 点击通知后,动作类型
operationType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
// 通过wantAgent模块的getWantAgent方法获取WantAgent对象
try {
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
try {
backgroundTaskManager.startBackgroundRunning(featureAbility.getContext(),
backgroundTaskManager.BackgroundMode.DATA_TRANSFER, wantAgentObj).then(() => {
console.info("Operation startBackgroundRunning succeeded");
}).catch((err) => {
console.error("Operation startBackgroundRunning failed Cause: " + err);
});
} catch (error) {
console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
}
});
} catch (error) {
console.error(`Operation getWantAgent failed. code is ${error.code} message is ${error.message}`);
}
}
function stopContinuousTask() {
try {
backgroundTaskManager.stopBackgroundRunning(featureAbility.getContext()).then(() => {
console.info("Operation stopBackgroundRunning succeeded");
}).catch((err) => {
console.error("Operation stopBackgroundRunning failed Cause: " + err);
});
} catch (error) {
console.error(`Operation stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
}
}
async function processAsyncJobs() {
// 此处执行具体的长时任务。
// 长时任务执行完,调用取消接口,释放资源。
stopContinuousTask();
}
let mMyStub;
class MyStub extends rpc.RemoteObject {
constructor(des) {
if (typeof des === 'string') {
super(des);
} else {
return null;
}
}
onRemoteRequest(code, data, reply, option) {
console.log('ServiceAbility onRemoteRequest called');
// code 的具体含义用户自定义
if (code === 1) {
// 接收到申请长时任务的请求码
startContinuousTask();
// 此处执行具体长时任务
} else if (code === 2) {
// 接收到取消长时任务的请求码
stopContinuousTask();
} else {
console.log('ServiceAbility unknown request code');
}
return true;
}
}
export default {
onStart() {
console.info('ServiceAbility onStart');
mMyStub = new MyStub("ServiceAbility-test");
// 在执行后台长时任前,调用申请接口。
startContinuousTask();
processAsyncJobs();
},
onStop() {
console.info('ServiceAbility onStop');
},
onConnect(want) {
console.info('ServiceAbility onConnect');
return mMyStub;
},
onReconnect(want) {
console.info('ServiceAbility onReconnect');
},
onDisconnect() {
console.info('ServiceAbility onDisconnect');
},
onCommand(want, startId) {
console.info('ServiceAbility onCommand');
}
};
```
## 相关实例
基于后台任务管理,有以下相关实例可供参考:
- [`ContinuousTask`:长时任务(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/TaskManagement/ContinuousTask)
\ No newline at end of file
# 长时任务
## 概述
### 功能介绍
应用退至后台后,在后台需要长时间运行用户可感知的任务,如播放音乐、导航等。为防止应用进程被挂起,导致对应功能异常,可以申请长时任务,使应用在后台长时间运行。
申请长时任务后,系统会做相应的校验,确保应用在执行相应的长时任务。同时,系统有与长时任务相关联的通知栏消息,用户删除通知栏消息时,系统会自动停止长时任务。
### 使用场景
下表给出了当前长时任务支持的类型,包含数据传输、音频播放、录音、定位导航、蓝牙相关、多设备互联、WLAN相关、音视频通话和计算任务。可以参考下表中的场景举例,选择合适的长时任务类型。
**表1** 长时任务类型
| 参数名 | 描述 | 场景举例 |
| -------- | -------- | -------- |
| DATA_TRANSFER | 数据传输 | 后台下载大文件,如浏览器后台下载等。 |
| AUDIO_PLAYBACK | 音频播放 | 音乐类应用在后台播放音乐。 |
| AUDIO_RECORDING | 录音 | 录音机在后台录音。 |
| LOCATION | 定位导航 | 导航类应用后台导航。 |
| BLUETOOTH_INTERACTION | 蓝牙相关 | 通过蓝牙传输分享的文件。 |
| MULTI_DEVICE_CONNECTION | 多设备互联 | 分布式业务连接。 |
| WIFI_INTERACTION | WLAN相关(仅对系统应用开放) | 通过WLAN传输分享的文件。 |
| VOIP | 音视频通话(仅对系统应用开放) | 系统聊天类应用后台音频电话。 |
| TASK_KEEPING | 计算任务(仅对特定设备开放) | 杀毒软件 |
- 申请了DATA_TRANSFER(数据传输)的长时任务,系统仅会提升应用进程的优先级,降低系统终止应用进程的概率,但仍然会挂起对应的应用进程。对于上传下载对应的功能,需要调用系统[上传下载代理接口](../reference/apis/js-apis-request.md)托管给系统执行。
- 申请了AUDIO_PLAYBACK(音频播放)的长时任务,要实现后台播放的功能,需要同时申请[媒体会话](../media/avsession-overview.md)
### 约束与限制
- **申请限制**:Stage模型中,长时任务仅支持UIAbility申请;FA模型中,长时任务仅支持ServiceAbility申请。
- **数量限制**:一个UIAbility(FA模型则为ServiceAbility)同一时刻仅支持申请一个长时任务,即在一个长时任务结束后才可能继续申请。如果一个应用同时需要申请多个长时任务,需要创建多个UIAbility;一个应用的一个UIAbility申请长时任务后,整个应用下的所有进程均不会被挂起。
- **运行限制**:系统会进行长时任务校验。若应用申请了长时任务,但未真正执行申请类型的长时任务或申请类型的任务已结束,系统会对应用进行管控。例如系统检测到应用申请了AUDIO_PLAYBACK(音频播放),但实际未播放音乐,系统则会终止对应的进程。
> **说明:**
>
> 应用按需求申请长时任务,当应用无需在后台运行(任务结束)时,要及时主动取消长时任务,否则系统会强行取消。例如用户点击音乐暂停播放时,应用需及时取消对应的长时任务;用户再次点击音乐播放时,需重新申请长时任务。
## 接口说明
**表2** 主要接口
以下是长时任务开发使用的相关接口,下表均以Promise形式为例,更多接口及使用方式请见[后台任务管理](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md)
| 接口名 | 描述 |
| -------- | -------- |
| startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: [WantAgent](../reference/apis/js-apis-app-ability-wantAgent.md)): Promise&lt;void&gt; | 申请长时任务 |
| stopBackgroundRunning(context: Context): Promise&lt;void&gt; | 取消长时任务 |
## 开发步骤
### Stage模型
1. 需要申请ohos.permission.KEEP_BACKGROUND_RUNNING权限,配置方式请参见[配置文件声明](../security/accesstoken-guidelines.md#配置文件权限声明)
2. 声明后台模式类型。
在module.json5配置文件中为需要使用长时任务的UIAbility声明相应的长时任务类型。
```ts
"module": {
"abilities": [
{
"backgroundModes": [
"audioRecording"
], // 后台模式类型
}
],
...
}
```
3. 导入模块。
```ts
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import wantAgent from '@ohos.app.ability.wantAgent';
```
4. 申请和取消长时任务。
- 在Stage模型中,长时任务支持设备本应用申请,也支持跨设备或跨应用申请。
- 跨设备或者跨应用在后台执行长时任务时,可以通过Call的方式在后台创建并运行UIAbility,具体使用请参考[Call调用开发指南(同设备)](../application-models/uiability-intra-device-interaction.md#通过call调用实现uiability交互仅对系统应用开放)[Call调用开发指南(跨设备)](../application-models/hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)
**设备本应用**申请长时任务示例代码如下:
```ts
@Entry
@Component
struct Index {
@State message: string = 'ContinuousTask';
// 通过getContext方法,来获取page所在的UIAbility上下文。
private context = getContext(this);
startContinuousTask() {
let wantAgentInfo = {
// 点击通知后,将要执行的动作列表
wants: [
{
bundleName: "com.example.myapplication",
abilityName: "com.example.myapplication.MainAbility"
}
],
// 点击通知后,动作类型
operationType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
// 通过wantAgent模块下getWantAgent方法获取WantAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
try {
backgroundTaskManager.startBackgroundRunning(this.context,
backgroundTaskManager.BackgroundMode.AUDIO_RECORDING, wantAgentObj).then(() => {
console.info(`Succeeded in operationing startBackgroundRunning.`);
}).catch((err) => {
console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
} catch (error) {
console.error(`Failed to start background running. Code is ${error.code} message is ${error.message}`);
}
});
}
stopContinuousTask() {
try {
backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
console.info(`Succeeded in operationing stopBackgroundRunning.`);
}).catch((err) => {
console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
} catch (error) {
console.error(`Failed to stop background running. Code is ${error.code} message is ${error.message}`);
}
}
build() {
Row() {
Column() {
Text("Index")
.fontSize(50)
.fontWeight(FontWeight.Bold)
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();
})
}
.width('100%')
}
.height('100%')
}
}
```
**跨设备或跨应用**申请长时任务示例代码如下:
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
const MSG_SEND_METHOD: string = 'CallSendMsg'
let mContext = null;
function startContinuousTask() {
let wantAgentInfo = {
// 点击通知后,将要执行的动作列表
wants: [
{
bundleName: "com.example.myapplication",
abilityName: "com.example.myapplication.MainAbility",
}
],
// 点击通知后,动作类型
operationType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
// 通过wantAgent模块的getWantAgent方法获取WantAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
try {
backgroundTaskManager.startBackgroundRunning(mContext,
backgroundTaskManager.BackgroundMode.AUDIO_RECORDING, wantAgentObj).then(() => {
console.info(`Succeeded in operationing startBackgroundRunning.`);
}).catch((err) => {
console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
} catch (err) {
console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
}
});
}
function stopContinuousTask() {
try {
backgroundTaskManager.stopBackgroundRunning(mContext).then(() => {
console.info(`Succeeded in operationing stopBackgroundRunning.`);
}).catch((err) => {
console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
} catch (err) {
console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
}
}
class MyParcelable {
num: number = 0;
str: String = '';
constructor(num, string) {
this.num = num;
this.str = string;
}
marshalling(messageSequence) {
messageSequence.writeInt(this.num);
messageSequence.writeString(this.str);
return true;
}
unmarshalling(messageSequence) {
this.num = messageSequence.readInt();
this.str = messageSequence.readString();
return true;
}
}
function sendMsgCallback(data) {
console.info('BgTaskAbility funcCallBack is called ' + data)
let receivedData = new MyParcelable(0, '')
data.readParcelable(receivedData)
console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`)
// 可以根据Caller端发送的序列化数据的str值,执行不同的方法。
if (receivedData.str === 'start_bgtask') {
startContinuousTask()
} else if (receivedData.str === 'stop_bgtask') {
stopContinuousTask();
}
return new MyParcelable(10, 'Callee test');
}
export default class BgTaskAbility extends UIAbility {
onCreate(want, launchParam) {
console.info("[Demo] BgTaskAbility onCreate")
this.callee.on('test', sendMsgCallback);
try {
this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
} catch (error) {
console.error(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
}
mContext = this.context;
}
onDestroy() {
console.info('[Demo] BgTaskAbility onDestroy')
}
onWindowStageCreate(windowStage) {
console.info('[Demo] BgTaskAbility onWindowStageCreate')
windowStage.loadContent("pages/index").then((data) => {
console.info(`load content succeed with data ${JSON.stringify(data)}`)
}).catch((error) => {
console.error(`load content failed with error ${JSON.stringify(error)}`)
})
}
onWindowStageDestroy() {
console.info('[Demo] BgTaskAbility onWindowStageDestroy')
}
onForeground() {
console.info('[Demo] BgTaskAbility onForeground')
}
onBackground() {
console.info('[Demo] BgTaskAbility onBackground')
}
};
```
### FA模型中
1. 启动并连接ServiceAbility。
- 不需要与用户进行交互时,采用startAbility()方法启动ServiceAbility(具体使用请参考[ServiceAbility组件](../application-models/serviceability-overview.md),并在ServiceAbility的onStart回调方法中,调用长时任务的申请和取消接口。
- 需要与用户进行交互时(如播放音乐),采用connectAbility()方法启动并连接ServiceAbility(具体使用请参考[ServiceAbility组件](../application-models/serviceability-overview.md),在获取到服务的代理对象后,与服务进行通信,控制长时任务的申请和取消。
2. 配置权限和声明后台模式类型。
在config.json文件中配置长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING,配置方式请参见[配置文件声明](../security/accesstoken-guidelines.md#配置文件权限声明)。同时,为需要使用长时任务的ServiceAbility声明相应的长时任务类型。
```js
"module": {
"package": "com.example.myapplication",
"abilities": [
{
"backgroundModes": [
"audioRecording",
], // 后台模式类型
"type": "service" // ability类型为service
}
],
"reqPermissions": [
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING" // 长时任务权限
}
]
}
```
3. 导入模块。
```js
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import featureAbility from '@ohos.ability.featureAbility';
import wantAgent from '@ohos.app.ability.wantAgent';
import rpc from "@ohos.rpc";
```
4. 申请和取消长时任务。在 ServiceAbility 中,调用 startBackgroundRunning() 接口和 startBackgroundRunning() 接口实现长时任务的申请和取消。
```js
function startContinuousTask() {
let wantAgentInfo = {
// 点击通知后,将要执行的动作列表
wants: [
{
bundleName: "com.example.myapplication",
abilityName: "com.example.myapplication.MainAbility"
}
],
// 点击通知后,动作类型
operationType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
// 通过wantAgent模块的getWantAgent方法获取WantAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
try {
backgroundTaskManager.startBackgroundRunning(featureAbility.getContext(),
backgroundTaskManager.BackgroundMode.AUDIO_RECORDING, wantAgentObj).then(() => {
console.info(`Succeeded in operationing startBackgroundRunning.`);
}).catch((err) => {
console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
} catch (error) {
console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
}
});
}
function stopContinuousTask() {
try {
backgroundTaskManager.stopBackgroundRunning(featureAbility.getContext()).then(() => {
console.info(`Succeeded in operationing stopBackgroundRunning.`);
}).catch((err) => {
console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
} catch (error) {
console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
}
}
async function processAsyncJobs() {
// 此处执行具体的长时任务。
// 长时任务执行完,调用取消接口,释放资源。
stopContinuousTask();
}
let mMyStub;
class MyStub extends rpc.RemoteObject {
constructor(des) {
if (typeof des === 'string') {
super(des);
} else {
return null;
}
}
onRemoteRequest(code, data, reply, option) {
console.log('ServiceAbility onRemoteRequest called');
// code 的具体含义用户自定义
if (code === 1) {
// 接收到申请长时任务的请求码
startContinuousTask();
// 此处执行具体长时任务
} else if (code === 2) {
// 接收到取消长时任务的请求码
stopContinuousTask();
} else {
console.log('ServiceAbility unknown request code');
}
return true;
}
}
export default {
onStart(want) {
console.info('ServiceAbility onStart');
mMyStub = new MyStub("ServiceAbility-test");
// 在执行长时任务前,调用申请接口。
startContinuousTask();
processAsyncJobs();
},
onStop() {
console.info('ServiceAbility onStop');
},
onConnect(want) {
console.info('ServiceAbility onConnect');
return mMyStub;
},
onReconnect(want) {
console.info('ServiceAbility onReconnect');
},
onDisconnect() {
console.info('ServiceAbility onDisconnect');
},
onCommand(want, restart, startId) {
console.info('ServiceAbility onCommand');
}
};
```
## 相关实例
针对长时任务开发,有以下相关实例可供参考:
- [ContinuousTask:长时任务(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/TaskManagement/ContinuousTask)
\ No newline at end of file
# 能效资源申请(仅对系统特权应用开放)
## 概述
### 功能介绍
部分系统基础应用对外提供或者维持系统基本功能,需要长时间运行,如系统为了维持和服务器的连接,需要有一个默认长连接推送服务的应用每隔一小段时间给服务器发送心跳包,为了避免该应用进程被挂起,可以申请能效资源来保障业务的执行。
### 基本概念
- **能效资源申请接口** :单独为进程申请CPU等资源的接口,保障系统应用在后台执行的诉求。申请CPU资源后,则应用或进程不被挂起。
- **系统特权应用**:配置[runningResourcesApply特权](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-app-privilege-config-guide.md#可由设备厂商配置的特权)应用的系统应用。
### 约束与限制
- 能效资源仅支持系统特权应用使用。
- CPU支持按照进程维度或应用维度申请,其他资源仅支持按照应用维度申请。
## 接口说明
**表1** 申请能效资源主要接口
以下是能效资源开发使用的相关接口,更多接口及使用方式请见[后台任务管理](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md)
| 接口名 | 描述 |
| -------- | -------- |
| applyEfficiencyResources(request:EfficiencyResourcesRequest): void | 申请能效资源 |
| resetAllEfficiencyResources():void | 释放全部能效资源 |
**表2** 能效资源申请参数
| 名称 | 类型 | 必填 | 说明 |
| -------- | -------- | -------- | -------- |
| resourceTypes | number | 是 | 申请的资源类型 |
| isApply | boolean | 是 | 申请或释放资源<br/>- ture表示申请资源<br/>- false表示释放部分资源 |
| timeOut | number | 是 | 资源使用时间(ms) |
| isPersist | boolean | 否 | 是否为永久持有资源,默认为false<br/>- ture表示永久持有<br/>- false表示有限时间内持有 |
| isProcess | boolean | 否 | 进程或应用申请,默认为false<br/>- ture表示进程申请<br/>- false表示应用申请 |
| reason | string | 是 | 申请资源原因 |
**表3** 能效资源类型
| 参数名 | 值 | 描述 |
| -------- | -------- | -------- |
| CPU | 1 | CPU资源,申请后应用进程不被挂起 |
| COMMON_EVENT | 2 | 公共事件资源,申请后应用进程被挂起后,可以收到公共事件 |
| TIMER | 4 | 公共事件资源,申请后应用进程被挂起后,Timer仍然可以唤醒应用 |
| WORK_SCHEDULER | 8 | 延迟任务资源,申请后延迟任务管控变宽松 |
| BLUETOOTH | 16 | 蓝牙资源,申请后应用进程被挂起后,蓝牙相关事件仍然可以唤醒应用 |
| GPS | 32 | GPS资源,申请后应用进程被挂起后,GPS相关事件可以唤醒应用 |
| AUDIO | 64 | 音频资源,有音频播放时对应的应用进程不被挂起 |
## 开发步骤
1. 导入模块。
```js
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
```
2. 申请能效资源。
```js
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
// 应用需要在后台保持活动状态,不被挂起。
let request = {
resourceTypes: backgroundTaskManager.ResourceType.CPU, // 资源类型是CPU资源,保证应用进程不被挂起
isApply: true, // 释放资源
timeOut: 0, // 超时时间,超过超时时间后资源自动释放
reason: "apply", // 申请原因
isPersist: true, // 永久持有资源
isProcess: false, // 在应用级别申请
};
try {
backgroundTaskManager.applyEfficiencyResources(request);
console.info("Succeeded in invoking applyEfficiencyResources.");
} catch (error) {
console.error(`Failed to invoke applyEfficiencyResources. Code is ${error.code} message is ${error.message}`);
}
```
3. 释放能效资源。应用在后台完成工作后,及时释放资源,支持释放部分资源或全部资源。
```js
// 应用在后台完成了工作后,全部释放能效资源
try {
backgroundTaskManager.resetAllEfficiencyResources();
} catch (error) {
console.error(`Failed to invoke resetAllEfficiencyResources. Code is ${error.code} message is ${error.message}`);
}
//应用在后台完成了工作后,部分释放能效资源
let request = {
resourceTypes: backgroundTaskManager.ResourceType.CPU,
isApply: false, // 释放资源
timeOut: 0,
reason: "apply",
isPersist: true,
isProcess: false, // 在应用级别释放资源
};
try {
backgroundTaskManager.applyEfficiencyResources(request);
console.info("Succeeded in invoking applyEfficiencyResources.");
} catch (error) {
console.error(`Failed to invoke applyEfficiencyResources. Code is ${error.code} message is ${error.message}`);
}
```
> **说明:**
> 能效资源申请接口支持动态申请,在任务完成后,建议主动取消能效资源申请,以降低设备耗电速度、保障用户使用流畅度。
\ No newline at end of file
# 申请能效资源开发指导
## 场景说明
在实际的系统中,存在一些重要性高的系统应用,虽然此类应用相比普通应用具有一定的特权,但为了进一步平衡系统的功耗开销,这些应用同样需要支持在后台可被挂起。但对于系统特权应用,为了避免挂起后重要功能受到影响,提供了独立的能效资源申请接口,使这些特权应用可以在后台执行一些特殊的任务和使用特定的系统资源,例如在被挂起期间如果仍然希望能够收到系统公共事件,可以使用能效资源接口向系统申请使用公共事件资源。
对于需要升级为特权应用的,开发者需要合理评估自己的业务诉求,向应用中心提出申请。
## 约束与限制
仅支持系统应用。
## 接口说明
**表1** 申请能效资源主要接口
| 接口名 | 描述 |
| ---------------------------------------- | ---------- |
| applyEfficiencyResources(request: [EfficiencyResourcesRequest](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#efficiencyresourcesrequest)): boolean | 申请能效资源。 |
| resetAllEfficiencyResources():void | 释放申请的能效资源。 |
## 开发步骤
1、当特权应用需要在后台使用特殊资源时。向系统申请目标资源。
2、当资源使用完毕,需要及时释放。支持释放部分资源或全部资源。
```js
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}`);
}
```
# 后台代理提醒概述
为保障用户体验,OpenHarmony对后台应用进程进行了有序治理,应用程序不能随意驻留在后台,同时应用后台行为受到严格管理,防止恶意应用行为。但对于某些退居后台或已退出的应用,可能需要在指定的时刻,向用户发送一些业务提醒通知。例如购物类应用,希望在指定时间点提醒用户有优惠活动。为满足此类业务诉求,OpenHarmony提供后台代理提醒功能,在应用退居后台或退出后,计时和提醒通知功能被系统后台代理接管。
后台代理提醒业务类型:
- 倒计时类:基于倒计时的提醒功能,适用于短时的计时提醒业务。
- 日历类:基于日历的提醒功能,适用于较长时间的提醒业务。
- 闹钟类:基于时钟的提醒功能,适用于闹钟相关业务。
# 短时任务开发指导
## 场景说明
当应用退到后台默认有6到12秒的运行时长,超过该时间后,系统会将应用置为挂起状态。对于绝大多数应用,6到12秒的时间,足够执行一些重要的任务,但如果应用需要更多的时间,可以通过短时任务接口,扩展应用的执行时间。
建议不要等到应用退后台后,才调用[requestSuspendDelay()](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerrequestsuspenddelay)方法申请延迟挂起,而是应该在执行任何的耗时操作前,都应该调用该接口,向系统申明扩展应用的执行时间。
当应用在前台时,使用[requestSuspendDelay()](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerrequestsuspenddelay)方法,不会影响应用的短时任务配额。
根据需要调用[getRemainingDelayTime()](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagergetremainingdelaytime)接口获取应用程序进入挂起状态前的剩余时间。由于每个应用每天的短时任务配额时间有限,当执行完耗时任务后,应当及时调用[cancelSuspendDelay()](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagercancelsuspenddelay)接口取消延迟挂起的申请。
一些典型的耗时任务,例如保存一些状态数据到本地数据库、打开和处理一个大型文件、同步一些数据到应用的云端服务器等。
## 接口说明
**表1** 短时任务主要接口
| 接口名 | 描述 |
| ---------------------------------------- | ---------------------------------------- |
| requestSuspendDelay(reason:&nbsp;string,&nbsp;callback:&nbsp;Callback&lt;void&gt;):&nbsp;[DelaySuspendInfo](../reference/apis/js-apis-backgroundTaskManager.md#delaysuspendinfo) | 后台应用申请延迟挂起。<br/>延迟挂起时间一般情况下默认值为3分钟,低电量时默认值为1分钟。 |
| getRemainingDelayTime(requestId:&nbsp;number):&nbsp;Promise&lt;number&gt; | 获取应用程序进入挂起状态前的剩余时间。<br/>使用Promise形式返回。 |
| cancelSuspendDelay(requestId:&nbsp;number):&nbsp;void | 取消延迟挂起。 |
## 开发步骤
当应用需要开始执行一个耗时的任务时。调用短时任务申请接口,并且在任务执行完后,调用短时任务取消接口。
```js
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
let id; // 申请延迟挂起任务ID
let delayTime; // 本次申请延迟挂起任务的剩余时间
// 申请延迟挂起
function requestSuspendDelay() {
let myReason = 'test requestSuspendDelay'; // 申请原因
try {
let delayInfo = backgroundTaskManager.requestSuspendDelay(myReason, () => {
// 此回调函数执行,表示应用的延迟挂起申请即将超时,应用需要执行一些清理和标注工作,并取消延时挂起
console.info("[backgroundTaskManager] Request suspension delay will time out.");
backgroundTaskManager.cancelSuspendDelay(id);
})
id = delayInfo.requestId;
delayTime = delayInfo.actualDelayTime;
console.info("[backgroundTaskManager] The requestId is: " + id);
console.info("[backgroundTaskManager]The actualDelayTime is: " + delayTime);
} catch (error) {
console.error(`[backgroundTaskManager] requestSuspendDelay failed. code is ${error.code} message is ${error.message}`);
}
}
// 获取进入挂起前的剩余时间
async function getRemainingDelayTime() {
try {
await backgroundTaskManager.getRemainingDelayTime(id).then(res => {
console.log('[backgroundTaskManager] promise => Operation getRemainingDelayTime succeeded. Data: ' + JSON.stringify(res));
}).catch(error => {
console.error(`[backgroundTaskManager] promise => Operation getRemainingDelayTime failed. code is ${error.code} message is ${error.message}`);
})
} catch (error) {
console.error(`[backgroundTaskManager] promise => Operation getRemainingDelayTime failed. code is ${error.code} message is ${error.message}`);
}
}
// 取消延迟挂起
function cancelSuspendDelay() {
backgroundTaskManager.cancelSuspendDelay(id);
}
async function performingLongRunningTask() {
// 在执行具体的耗时任务前,调用短时任务申请接口。向系统申请延迟挂起,延长应用的后台执行时间。
requestSuspendDelay();
// 根据需要,通过剩余时间查询接口,获取可用时间配额。
await getRemainingDelayTime();
if (delayTime < 0) { // 如果时间配置少于一定的大小,考虑取消此次耗时操作。
// 处理短时任务配额时间不够的场景
cancelSuspendDelay();
return;
}
// 此处执行具体的耗时任务
// 耗时任务执行完,调用短时任务取消接口,避免配额浪费
cancelSuspendDelay();
}
```
## 相关实例
针对短时任务开发,有以下相关实例可供参考:
- [短时任务(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/TaskManagement/TransientTask)
# 短时任务
## 概述
应用退至后台一小段时间后,应用进程会被挂起,无法执行对应的任务。如果应用在后台仍需要执行耗时不长的任务,如状态保存等,可以通过本文申请短时任务,扩展应用在后台的运行时间。
### 约束与限制
- **申请时机**:应用需要在前台或退至后台5秒内,申请短时任务,否则会申请失败。
- **数量限制**:一个应用同一时刻最多申请3个短时任务。以图1为例,在①②③时间段内的任意时刻,应用申请了2个短时任务;在④时间段内的任意时刻,应用申请了1个短时任务。
- **配额机制**:一个应用会有一定的短时任务配额(根据系统状态和用户习惯调整),单日(24小时内)配额默认为10分钟,单次配额最大为3分钟,[低电量](../reference/apis/js-apis-battery-info.md)时单次配额默认为1分钟,配额消耗完后不允许再申请短时任务。同时,系统提供获取对应短时任务剩余时间的查询接口,用以查询本次短时任务剩余时间,以确认是否继续运行其他业务。
- **配额计算**:仅当应用在后台时,对应用下的短时任务计时;同一个应用下的同一个时间段的短时任务,不重复计时。以下图为例:应用有两个短时任务A和B,在前台时申请短时任务A,应用退至后台后开始计时为①,应用进入前台②后不计时,再次进入后台③后开始计时,短时任务A结束后,由于阶段④仍然有短时任务B,所以该阶段继续计时。因此,在这个过程中,该应用短时任务总耗时为①+③+④。
**图1** 短时任务配额计算原理图
![transient-task](figures/transient-task.png)
> **说明:**
>
> 任务完成后,应用需主动取消短时任务,否则会影响应用当日短时任务的剩余配额。
- **超时**:短时任务即将超时时,系统会回调应用,应用需要取消短时任务。如果超时不取消,系统会终止对应的应用进程。
## 接口说明
**表1** 主要接口
以下是短时任务开发使用的主要接口,更多接口及使用方式请见[后台任务管理](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md)
| 接口名 | 描述 |
| -------- | -------- |
| requestSuspendDelay(reason: string, callback: Callback&lt;void&gt;): DelaySuspendInfo | 申请短时任务。 |
| getRemainingDelayTime(requestId: number): Promise&lt;number&gt; | 获取对应短时任务的剩余时间。 |
| cancelSuspendDelay(requestId: number): void | 取消短时任务。 |
## 开发步骤
1. 导入模块。
```js
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
```
2. 申请短时任务并实现回调。
```js
let id; // 申请短时任务ID
let delayTime; // 本次申请短时任务的剩余时间
// 申请短时任务
function requestSuspendDelay() {
let myReason = 'test requestSuspendDelay'; // 申请原因
try {
let delayInfo = backgroundTaskManager.requestSuspendDelay(myReason, () => {
// 回调函数。应用申请的短时任务即将超时,通过此函数回调应用,执行一些清理和标注工作,并取消短时任务
console.info('Succeeded in requesting suspend delay.');
backgroundTaskManager.cancelSuspendDelay(id);
})
id = delayInfo.requestId;
delayTime = delayInfo.actualDelayTime;
} catch (err) {
console.error(`Failed to request suspend delay. Code: ${err.code}, message: ${err.message}`);
}
}
```
3. 获取短时任务剩余时间。查询本次短时任务的剩余时间,用以判断是否继续运行其他业务,例如应用有两个小任务,在执行完第一个小任务后,可以判断本次短时任务是否还有剩余时间来决定是否执行第二个小任务。
```js
async function getRemainingDelayTime() {
try {
backgroundTaskManager.getRemainingDelayTime(id).then(res => {
console.info('Succeeded in getting remaining delay time.');
}).catch(err => {
console.error(`Failed to get remaining delay time. Code: ${err.code}, message: ${err.message}`);
})
} catch (err) {
console.error(`Failed to get remaining delay time. Code: ${err.code}, message: ${err.message}`);
}
}
```
4. 取消短时任务。
```js
function cancelSuspendDelay() {
backgroundTaskManager.cancelSuspendDelay(id);
}
```
## 相关实例
针对短时任务开发,有以下相关实例可供参考:
- [短时任务(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/TaskManagement/TransientTask)
\ No newline at end of file
# 延迟任务调度开发指导
## 场景介绍
应用要执行对实时性要求不高的任务或持久性任务的时候,比如设备空闲时候做一次数据学习等场景,可以使用延迟调度任务,该机制在满足应用设定条件的时候,会根据系统当前状态,如内存、功耗、温度等统一决策调度时间,[WorkSchedulerExtensionAbility](./workscheduler-extensionability.md)提供了延迟任务回调拓展能力,注册延迟任务后需要实现延迟任务回调拓展能力。
延迟任务调度约束见[延迟任务调度约束](./background-task-overview.md#延迟任务调度约束)
## 接口说明
**表1** workScheduler主要接口
接口名 | 接口描述
---------------------------------------------------------|-----------------------------------------
startWork(work: WorkInfo): void; | 延迟调度任务申请
stopWork(work: WorkInfo, needCancel?: boolean): void; | 延迟调度任务取消
getWorkStatus(workId: number, callback: AsyncCallback\<WorkInfo>): void;| 获取延迟调度任务状态(Callback形式)
getWorkStatus(workId: number): Promise\<WorkInfo>; | 获取延迟调度任务状态(Promise形式)
obtainAllWorks(callback: AsyncCallback\<void>): Array\<WorkInfo>;| 获取所有延迟调度任务(Callback形式)
obtainAllWorks(): Promise<Array\<WorkInfo>>;| 获取所有延迟调度任务(Promise形式)
stopAndClearWorks(): void;| 停止并清除任务
isLastWorkTimeOut(workId: number, callback: AsyncCallback\<void>): boolean;| 获取上次任务是否超时(针对RepeatWork,Callback形式)
isLastWorkTimeOut(workId: number): Promise\<boolean>;| 获取上次任务是否超时(针对RepeatWork,Promise形式)
**表2** WorkInfo包含参数
WorkInfo设置参数约束见[延迟任务调度约束](./background-task-overview.md#延迟任务调度约束)
参数名| 类型 |描述
---------------------------------------------------------|-----------------------------------------|---------------------------------------------------------
workId| number | 延迟任务ID(必填)
bundleName| string | 延迟任务Bundle名称(必填)
abilityName| string | 延迟任务回调通知的组件名(必填)
networkType | [NetworkType](../reference/apis/js-apis-resourceschedule-workScheduler.md#networktype) | 网络类型
isCharging| boolean | 是否充电
chargerType| [ChargingType](../reference/apis/js-apis-resourceschedule-workScheduler.md#chargingtype) | 充电类型
batteryLevel| number | 电量
batteryStatus| [BatteryStatus](../reference/apis/js-apis-resourceschedule-workScheduler.md#batterystatus) | 电池状态
storageRequest| [StorageRequest](../reference/apis/js-apis-resourceschedule-workScheduler.md#storagerequest) |存储状态
isRepeat| boolean |是否循环任务
repeatCycleTime| number |循环间隔
repeatCount | number|循环次数
parameters | {[key: string]: number | string | boolean} |携带参数信息
**表3** 延迟任务回调接口
接口名 | 接口描述
---------------------------------------------------------|-----------------------------------------
onWorkStart(work: WorkInfo): void | 延迟调度任务开始回调
onWorkStop(work: WorkInfo): void | 延迟调度任务结束回调
### 开发步骤
1、导入模块。
注册相关接口包导入:
```js
import workScheduler from '@ohos.resourceschedule.workScheduler';
```
回调相关接口包导入:
```js
import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility';
```
2、开发对应的ExtensionAbility,用于回调执行具体的延迟任务。关于ExtensionAbility的介绍,参考[ExtensionAbility机制](../application-models/extensionability-overview.md)[WorkSchedulerExtensionAbility开发指导](./workscheduler-extensionability.md)
```ts
import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility';
export default class MyExtension extends WorkSchedulerExtensionAbility {
onWorkStart(workInfo) {
console.log('MyWorkSchedulerExtensionAbility onWorkStart' + JSON.stringify(workInfo));
}
onWorkStop(workInfo) {
console.log('MyWorkSchedulerExtensionAbility onWorkStop' + JSON.stringify(workInfo));
}
}
```
3、注册延迟任务
```ts
import workScheduler from '@ohos.resourceschedule.workScheduler';
let workInfo = {
workId: 1,
batteryStatus:workScheduler.BatteryStatus.BATTERY_STATUS_LOW,
isRepeat: false,
isPersisted: true,
bundleName: "com.example.myapplication",
abilityName: "MyExtension",
parameters: {
mykey0: 1,
mykey1: "string value",
mykey2: true,
mykey3: 1.5
}
}
try{
workScheduler.startWork(workInfo);
console.info('workschedulerLog startWork success');
} catch (error) {
console.error(`workschedulerLog startwork failed. code is ${error.code} message is ${error.message}`);
}
```
4、取消延迟任务
```ts
import workScheduler from '@ohos.resourceschedule.workScheduler';
let workInfo = {
workId: 1,
batteryStatus:workScheduler.BatteryStatus.BATTERY_STATUS_LOW,
isRepeat: false,
isPersisted: true,
bundleName: "com.example.myapplication",
abilityName: "MyExtension",
parameters: {
mykey0: 1,
mykey1: "string value",
mykey2: true,
mykey3: 1.5
}
}
try{
workScheduler.stopWork(workInfo, false);
console.info('workschedulerLog stopWork success');
} catch (error) {
console.error(`workschedulerLog stopWork failed. code is ${error.code} message is ${error.message}`);
}
```
5、获取指定延迟任务
```ts
try{
workScheduler.getWorkStatus(50, (error, res) => {
if (error) {
console.error(`workschedulerLog getWorkStatus failed. code is ${error.code} message is ${error.message}`);
} else {
for (let item in res) {
console.info(`workschedulerLog getWorkStatus success, ${item} is: ${res[item]}`);
}
}
});
} catch (error) {
console.error(`workschedulerLog getWorkStatus failed. code is ${error.code} message is ${error.message}`);
}
```
6、获取所有延迟任务
```ts
try{
workScheduler.obtainAllWorks((error, res) =>{
if (error) {
console.error(`workschedulerLog obtainAllWorks failed. code is ${error.code} message is ${error.message}`);
} else {
console.info(`workschedulerLog obtainAllWorks success, data is: ${JSON.stringify(res)}`);
}
});
} catch (error) {
console.error(`workschedulerLog obtainAllWorks failed. code is ${error.code} message is ${error.message}`);
}
```
7、停止并清除任务
```ts
try{
workScheduler.stopAndClearWorks();
console.info(`workschedulerLog stopAndClearWorks success`);
} catch (error) {
console.error(`workschedulerLog stopAndClearWorks failed. code is ${error.code} message is ${error.message}`);
}
```
8、判断上次执行是否超时
```ts
try{
workScheduler.isLastWorkTimeOut(500, (error, res) =>{
if (error) {
console.error(`workschedulerLog isLastWorkTimeOut failed. code is ${error.code} message is ${error.message}`);
} else {
console.info(`workschedulerLog isLastWorkTimeOut success, data is: ${res}`);
}
});
} catch (error) {
console.error(`workschedulerLog isLastWorkTimeOut failed. code is ${error.code} message is ${error.message}`);
}
```
## 相关实例
基于延迟任务调度,有以下相关实例可供参考:
- [`WorkScheduler`:任务延时调度(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/TaskManagement/WorkScheduler)
\ No newline at end of file
# 延迟任务
## 概述
### 功能介绍
应用退至后台后,需要执行实时性要求不高的任务,例如有网络时不定期主动获取邮件等,可以使用延迟任务。当应用满足设定条件(包括网络类型、充电类型、存储状态、电池状态、定时状态等)时,将任务添加到执行队列,系统会根据内存、功耗、设备温度、用户使用习惯等统一调度拉起应用。
### 运行原理
**图1** 延迟任务实现原理
![WorkScheduler](figures/WorkScheduler.png)
应用调用延迟任务接口注册、删除、查询延迟任务,延迟任务管理模块会根据任务设置的条件(通过WorkInfo参数设置,包括网络类型、充电类型、存储状态等)和系统状态(包括内存、功耗、设备温度、用户使用习惯等)统一决策调度时机。
当满足调度条件或调度结束时,系统会回调应用[WorkSchedulerExtensionAbility](../reference/apis/js-apis-WorkSchedulerExtensionAbility.md)中 onWorkStart() 或 onWorkStop() 的方法,同时会为应用单独创建一个Extension扩展进程用以承载[WorkSchedulerExtensionAbility](../reference/apis/js-apis-WorkSchedulerExtensionAbility.md),并给[WorkSchedulerExtensionAbility](../reference/apis/js-apis-WorkSchedulerExtensionAbility.md)一定的活动周期,开发者可以在对应回调方法中实现自己的任务逻辑。
### 约束与限制
- **数量限制**:一个应用同一时刻最多申请10个延迟任务。
- **执行频率限制**:系统会根据[应用的活跃分组](../reference/apis/js-apis-resourceschedule-deviceUsageStatistics.md),对延迟任务做分级管控,限制延迟任务调度的执行频率。通过能效资源接口申请了WORK_SCHEDULER资源的应用,会被放在能效资源豁免分组中。
**表1** 应用活跃程度分组
| 应用活跃分组 | 延迟任务执行频率 |
| -------- | -------- |
| 活跃分组 | 最小间隔2小时 |
| 经常使用分组 | 最小间隔4小时 |
| 常用使用 | 最小间隔24小时 |
| 极少使用分组 | 最小间隔48小时 |
| 受限使用分组 | 禁止 |
| 从未使用分组 | 禁止 |
| 能效资源豁免分组 | 不受限制 |
- **超时**:WorkSchedulerExtensionAbility单次回调最长运行2分钟。如果超时不取消,系统会终止对应的Extension进程。对于系统特权应用,可以通过能效资源接口申请WORK_SCHEDULER资源,扩展单次回调运行时长,扩展后在充电状态下为20分钟,非充电状态下为10分钟。
- **调度延迟**:系统会根据内存、功耗、设备温度、用户使用习惯等统一调度,如当系统内存资源不足或温度达到一定挡位时,系统将延迟调度该任务。
- **WorkSchedulerExtensionAbility接口调用限制**:为实现对WorkSchedulerExtensionAbility能力的管控,在WorkSchedulerExtensionAbility中限制以下接口的调用:
[@ohos.resourceschedule.backgroundTaskManager (后台任务管理)](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md)
[@ohos.backgroundTaskManager (后台任务管理)](../reference/apis/js-apis-backgroundTaskManager.md)
[@ohos.multimedia.camera (相机管理)](../reference/apis/js-apis-camera.md)
[@ohos.multimedia.audio (音频管理)](../reference/apis/js-apis-audio.md)
[@ohos.multimedia.media (媒体服务)](../reference/apis/js-apis-media.md)
## 接口说明
**表2** 延迟任务主要接口
以下是延迟任务开发使用的相关接口,更多接口及使用方式请见[延迟任务](../reference/apis/js-apis-resourceschedule-workScheduler.md)文档。
| 接口名 | 接口描述 |
| -------- | -------- |
| startWork(work: WorkInfo): void; | 申请延迟任务 |
| stopWork(work: WorkInfo, needCancel?: boolean): void; | 取消延迟任务 |
| getWorkStatus(workId: number, callback: AsyncCallback&lt;WorkInfo&gt;): void; | 获取延迟任务状态(Callback形式) |
| getWorkStatus(workId: number): Promise&lt;WorkInfo&gt;; | 获取延迟任务状态(Promise形式) |
| obtainAllWorks(callback: AsyncCallback&lt;void&gt;): Array&lt;WorkInfo&gt;; | 获取所有延迟任务(Callback形式) |
| obtainAllWorks(): Promise&lt;Array&lt;WorkInfo&gt;&gt;; | 获取所有延迟任务(Promise形式) |
| stopAndClearWorks(): void; | 停止并清除任务 |
| isLastWorkTimeOut(workId: number, callback: AsyncCallback&lt;void&gt;): boolean; | 获取上次任务是否超时(针对RepeatWork,Callback形式) |
| isLastWorkTimeOut(workId: number): Promise&lt;boolean&gt;; | 获取上次任务是否超时(针对RepeatWork,Promise形式) |
**表3** WorkInfo参数
| 参数名 | 类型 | 描述 |
| -------- | -------- | -------- |
| workId | number | 延迟任务Id(必填) |
| bundleName | string | 延迟任务包名(必填) |
| abilityName | string | 延迟任务回调通知的组件名(必填) |
| networkType | [NetworkType](../reference/apis/js-apis-resourceschedule-workScheduler.md#networktype) | 网络类型 |
| isCharging | boolean | 是否充电 |
| chargerType | [ChargingType](../reference/apis/js-apis-resourceschedule-workScheduler.md#chargingtype) | 充电类型 |
| batteryLevel | number | 电量 |
| batteryStatus | [BatteryStatus](../reference/apis/js-apis-resourceschedule-workScheduler.md#batterystatus) | 电池状态 |
| storageRequest | [StorageRequest](../reference/apis/js-apis-resourceschedule-workScheduler.md#storagerequest) | 存储状态 |
| isRepeat | boolean | 是否循环任务 |
| repeatCycleTime | number | 循环间隔 |
| repeatCount | number | 循环次数 |
| parameters | [key: string]: number | 携带参数信息 |
WorkInfo参数用于设置应用条件,参数设置时需遵循以下规则:
- workId、bundleName、abilityName为必填项,bundleName需为本应用包名。
- 携带参数信息仅支持number、string、bool三种类型。
- 至少设置一个满足的条件,包括网络类型、充电类型、存储状态、电池状态、定时状态等。
- 对于重复任务,任务执行间隔至少20分钟。设置重复任务时间间隔时,须同时设置是否循环或循环次数中的一个。
**表4** 延迟任务回调接口
以下是延迟任务回调开发使用的相关接口,更多接口及使用方式请见[延迟任务回调](../reference/apis/js-apis-WorkSchedulerExtensionAbility.md)文档。
| 接口名 | 接口描述 |
| -------- | -------- |
| onWorkStart(work: WorkInfo): void | 延迟调度任务开始的回调 |
| onWorkStop(work: WorkInfo): void | 延迟调度任务结束的回调 |
## 开发步骤
延迟任务调度开发步骤分为两步:实现延迟任务调度扩展能力、实现延迟任务调度。
1. **延迟任务调度扩展能力**:实现WorkSchedulerExtensionAbility开始和结束的回调接口。
2. **延迟任务调度**:调用延迟任务接口,实现延迟任务申请、取消等功能。
### 实现延迟任务回调拓展能力
1. 新建工程目录。
在工程entry Module对应的ets目录(./entry/src/main/ets)下,新建目录及ArkTS文件,例如新建一个目录并命名为extension。在extension目录下,新建一个ArkTS文件并命名为WorkSchedulerExtension.ets,用以实现延迟任务回调接口。
2. 导入模块。
```ts
import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility';
```
3. 实现WorkSchedulerExtension生命周期接口。
```ts
export default class MyWorkSchedulerExtensionAbility extends WorkSchedulerExtensionAbility {
// 延迟任务开始回调
onWorkStart(workInfo) {
console.info(`onWorkStart, workInfo = ${JSON.stringify(workInfo)}`);
}
// 延迟任务结束回调
onWorkStop(workInfo) {
console.info(`onWorkStop, workInfo is ${JSON.stringify(workInfo)}`);
}
}
```
4.[module.json5配置文件](../quick-start/module-configuration-file.md)中注册WorkSchedulerExtensionAbility,并设置如下标签:
- type标签设置为“workScheduler”。
- srcEntry标签设置为当前ExtensionAbility组件所对应的代码路径。
```json
{
"module": {
"extensionAbilities": [
{
"name": "MyWorkSchedulerExtensionAbility",
"srcEntry": "./ets/WorkSchedulerExtension/WorkSchedulerExtension.ts",
"label": "$string:WorkSchedulerExtensionAbility_label",
"description": "$string:WorkSchedulerExtensionAbility_desc",
"type": "workScheduler"
}
]
}
}
```
### 实现延迟任务调度
1. 导入模块。
```ts
import workScheduler from '@ohos.resourceschedule.workScheduler';
```
2. 申请延迟任务。
```ts
private workInfo = {
workId: 1,
networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI,
bundleName: 'com.example.application',
abilityName: 'MyWorkSchedulerExtensionAbility'
}
try {
workScheduler.startWork(this.workInfo);
console.info(`startWork success`);
} catch (error) {
console.error(`startWork failed. code is ${error.code} message is ${error.message}`);
}
```
3. 取消延迟任务。
```ts
private workInfo = {
workId: 1,
networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI,
bundleName: 'com.example.application',
abilityName: 'MyWorkSchedulerExtensionAbility'
}
try {
workScheduler.stopWork(this.workInfo);
console.info(`stopWork success`);
} catch (error) {
console.error(`stopWork failed. code is ${error.code} message is ${error.message}`);
}
```
## 相关实例
针对延迟任务调度的开发,有以下相关示例可供参考:
- [WorkScheduler的创建与使用(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/TaskManagement/WorkScheduler)
\ No newline at end of file
# 延迟任务回调能力开发指导(WorkSchedulerExtensionAbility)
对于实时性要求不高的任务或持久性任务,可以使用延迟任务,该机制会在应用满足应用设定条件(包括网络类型、充电类型、存储状态、电池状态、定时状态)时,根据系统当前状态,由系统统一决策调度时间。
WorkSchedulerExtensionAbility提供了延迟任务回调能力,在延迟任务开始和结束时,系统会调用回调接口来处理任务逻辑,开发者可在回调接口里面编写自己的任务逻辑。
## 运作机制
延迟任务调度运作机制如图1所示。
**图1** 延迟任务调度运作机制 
![WorkSchedulerExtensionAbility](figures/WorkSchedulerExtensionAbility.png)
应用通过[延迟任务API](../reference/apis/js-apis-resourceschedule-workScheduler.md)注册、删除、查询任务。
应用服务侧进行条件检测和判断,若满足条件,则回调WorkSchedulerExtensionAbility拉起应用,执行onWorkStart、onWorkStop回调接口。
## 接口说明
WorkSchedulerExtensionAbility类拥有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-WorkSchedulerExtensionAbility.md)
| 接口名 | 描述 |
| -------- | -------- |
| onWorkStart(work: workScheduler.WorkInfo): void | 延迟任务调度开始回调。 |
| onWorkStop(work: workScheduler.WorkInfo): void | 延迟任务调度结束回调。 |
## 开发步骤
在DevEco Studio工程中新建一个WorkScheduler工程,主要涉及如下关键步骤:
- [实现延迟任务回调拓展能力](#实现延迟任务回调拓展能力):开发延迟任务生命周期回调接口WorkSchedulerExtensionAbility。
- [实现延迟任务调度能力](#实现延迟任务调度能力):开发延迟任务API,实现延迟任务注册、停止等功能。
- [配置文件](#配置文件):配置应用配置文件module.json5。
### 实现延迟任务回调拓展能力
1. 在工程根目录新建Module,模板选择为Ohos Library,命名为library。
2. 在library对应的ets目录(./library/src/main/ets)下,新建ArkTS文件并命名为workAbility.ets,用于实现延迟任务回调接口。
导入模块。
```ts
import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility';
```
实现WorkSchedulerExtension生命周期接口。
```ts
export default class workAbility extends WorkSchedulerExtensionAbility {
// 延迟任务开始回调
onWorkStart(workInfo) {
console.log(`onWorkStart CommonEvent publish start ${JSON.stringify(workInfo)}`);
// 发送升级通知
let notificationRequest = notification.getNotificationContentBasic('upgrade', upgradeMessage, '');
notification.publish(notificationRequest, (err) => {
if (err) {
console.log(`onWorkStart notification publish err ${JSON.stringify(err)}`);
}
console.log(`onWorkStart notification publish success`);
});
}
// 延迟任务结束回调
onWorkStop(workInfo) {
// 发送升级完成通知
let notificationRequest = notification.getNotificationContentBasic('upgrade', 'upgrade success', '');
notification.publish(notificationRequest, (err) => {
if (err) {
console.log(`onWorkStop notification publish err ${JSON.stringify(err)}`);
}
console.log(`onWorkStop notification publish success`);
});
}
}
```
3. 在工程entry Module对应的ets目录(./entry/src/main/ets)下,新建一个目录并命名为workAbility。
在workAbility目录下,新建一个ArkTS文件并命名为WorkTest.ets,实现延迟任务回调接口。
导入模块。
```ts
import { workAbility } from '@ohos/library'
```
继承workAbility,实现WorkSchedulerExtension生命周期接口。
```ts
export default class WorkTest extends workAbility {
onWorkStart(workInfo) {
console.log(`onWorkStartTest start ${JSON.stringify(workInfo)}`);
super.onWorkStart(workInfo);
}
onWorkStopTest(workInfo) {
super.onWorkStop(workInfo);
console.log(`onWorkStop value`);
}
}
```
### 实现延迟任务调度能力
1. 在library对应的ets目录(./library/src/main/ets)下,新建TypeScript文件并命名为DelayWork.ts,用于实现延迟任务API。
导入模块。
```ts
import workScheduler from '@ohos.resourceschedule.workScheduler';
```
封装延迟任务注册、停止接口。
```ts
export default class DelayWork {
private workInfo = {
workId: 1,
networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI,
bundleName: '',
abilityName: ''
}
// 注册延迟任务
startWork(bundleName: string, abilityName: string) {
this.workInfo.bundleName = bundleName;
this.workInfo.abilityName = abilityName;
try {
workScheduler.startWork(this.workInfo);
console.log(`startWork success`);
} catch (error) {
Logger.error(TAG, `startWork startwork failed. code is ${error.code} message is ${error.message}`);
prompt.showToast({
message: `${error.message}`
});
}
}
// 停止延迟任务
stopWork(bundleName: string, abilityName: string) {
this.workInfo.bundleName = bundleName;
this.workInfo.abilityName = abilityName;
workScheduler.stopWork(this.workInfo, false);
console.log(`stopWork`);
}
}
```
2. 在工程entry Module对应的index页面(./entry/src/main/ets/pages/index.ets)下,增加“升级”按钮,调用library封装的延迟任务注册接口。
导入模块。
```ts
import { workAbility } from '@ohos/library';
```
增加“升级”按钮,调用library封装的延迟任务注册接口,传入bundleName和abilityName,其中abilityName为WorkTest。
```ts
Button($r('app.string.upgrade'))
.width('60%')
.height(40)
.fontSize(30)
.onClick(() => {
this.work.startWork('ohos.samples.workscheduler', 'WorkTest');
});
```
在组件析构时,调用延迟任务停止接口。
```ts
aboutToDisappear() {
this.work.stopWork('ohos.samples.workscheduler', 'WorkTest');
}
```
### 配置文件
1. 在工程entry Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册WorkSchedulerExtensionAbility,type标签需要设置为“workScheduler”,srcEntry标签表示当前ExtensionAbility组件所对应的代码路径。
```json
{
"module": {
"extensionAbilities": [
{
"name": "WorkTest",
"srcEntry": "./ets/workAbility/WorkTest.ets",
"label": "$string:WorkSchedulerExtensionAbility_label",
"description": "$string:WorkSchedulerExtensionAbility_desc",
"type": "workScheduler"
}
]
}
}
```
## 限制
为了降低WorkSchedulerExtensionAbility能力被三方应用滥用的风险,在WorkSchedulerExtensionAbility中限制以下接口的调用
- @ohos.backgroundTaskManager.d.ts
- @ohos.resourceschedule.backgroundTaskManager.d.ts
- @ohos.multimedia.camera.d.ts
- @ohos.multimedia.audio.d.ts
- @ohos.multimedia.media.d.ts
## 相关实例
针对WorkSchedulerExtensionAbility开发,有以下相关示例可供参考:
- [WorkScheduler的创建与使用(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/TaskManagement/WorkScheduler)
......@@ -597,16 +597,12 @@
- [设置分布式文件数据等级](file-management/set-security-label.md)
- [跨设备文件访问](file-management/file-access-across-devices.md)
- 后台任务(Background Task)管理
- 后台任务
- [后台任务概述](task-management/background-task-overview.md)
- [短时任务开发指导](task-management/transient-task-dev-guide.md)
- [长时任务开发指导](task-management/continuous-task-dev-guide.md)
- [延迟任务调度开发指导](task-management/work-scheduler-dev-guide.md)
- [延迟任务回调能力开发指导(WorkSchedulerExtensionAbility)](task-management/workscheduler-extensionability.md)
- [申请能效资源开发指导](task-management/efficiency-resources-apply-dev-guide.md)
- 后台代理提醒
- [后台代理提醒概述](task-management/reminder-agent-overview.md)
- [后台代理提醒开发指导](task-management/reminder-agent-development.md)
- [短时任务](task-management/transient-task.md)
- [长时任务](task-management/continuous-task.md)
- [延迟任务](task-management/work-scheduler.md)
- [代理提醒](task-management/agent-powered-reminder.md)
- [能效资源申请(仅对系统特权应用开放)](task-management/efficiency-resource-request.md)
- 设备管理
- USB服务
- [USB服务开发概述](device/usb-overview.md)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册