OpenHarmony后台任务和Worker的用法详解
OpenHarmony后台任务和Worker的用法详解 雷琦玮、葛凌军 Huawei OpenHarmony Enable Service 前言 在OpenHarmony中,后台任务分为两种类型: 一种是指当前应用或业务模块处于后台(无可见界面)时,有需要继续执行或者后续执行的任务。 另一种是指当前应用或业务处于前台(有可见界面)时,除开主线程执行的任务,还需要别的线程同时执行其他
前言
在OpenHarmony中,后台任务分为两种类型: 一种是指当前应用或业务模块处于后台(无可见界面 另一种是指当前应用或业务处于前台(有可见界面)时,除开主线程执行的任务,还需要别的线程同时执行其他的任务。 下面,我们会从这两方面分开讲解一下他们的基本用法。
后台任务(无可见界面)
后台应用频繁活动,会造成用户设备耗电快、卡顿等现象。因此,为了支撑性能、功耗诉求,系统仅允许应用在后台执行规范内的活动,规范外的活动默认会被挂起,当资源不足时会被回收。 针对应用或业务模块处于后台(无可见界面)时,有需要继续执行或者后续执行的业务,可基于业务类型,申请延迟挂起或者避免进入挂起状态;使用,执行对实时性要求不高的任务;同时针对特权应用,如果需要更加灵活的配置,可以申请。
OpenHarmony将后台任务分为四种类型,并提供了一个资源申请的扩展功能:
无后台业务 :应用或业务模块退到后台后,无任务需要处理。
短时任务 :应用或业务模块退到后台后,如果有紧急不可推迟且短时间能完成的任务,如应用退后台要进行数据压缩,不可中断,则使用短时任务申请延迟进入挂起(Suspend)状态。
长时任务 :如果是用户发起的可感知业务需要长时间后台运行,如后台播放音乐、导航、设备连接、VoIP等,则使用长时任务避免进入挂起(Suspend)状态。
延迟任务 :延迟任务调度给应用提供一个机制,允许应用根据系统安排,在系统空闲时执行实时性不高的任务。当满足设定条件的时候,任务会被放入待调度队列,当系统空闲时调度该任务。
能效资源 :能效资源包括CPU资源、WORK_SCHEDULER资源、软件资源(COMMON_EVENT, TIMER)、硬件资源(GPS, BLUETOOTH)。如果应用或者进程申请了能效资源,那么根据能效资源的类型会拥有相应的特权,例如申请了CPU资源的可以不被挂起,申请了WORK_SCHEDULER后延时任务可以拥有更长的执行时间。
短时任务
退到后台的应用有不可中断且短时间能完成的任务时,可以使用短时任务机制。该机制允许应用在后台短时间内完成任务,保障应用业务运行不受后台生命周期管理的影响。
说明: 短时任务仅针对应用的临时任务提供资源使用生命周期保障,限制单次最大使用时长为3分钟,全天使用配额默认为10分钟(具体时长系统根据应用场景和系统状态智能调整)。
短时任务使用约束
短时任务的使用需要遵从如下约束和规则:
-
申请时机:允许应用在前台时,或退后台在被挂起之前(应用退到后台默认有6~12秒的运行时长,具体时长由系统根据具体场景决定)申请延迟挂起,否则可能被挂起(Suspend),导致申请失败。
-
超时:延迟挂起即将超时(Timeout),系统通过回调知会应用,应用需要取消对应的延迟挂起。如果超时不取消,该应用会被强制终止。
-
取消时机:任务完成后,应用应主动取消延迟挂起,不要等到系统回调后再取消,否则会影响该应用的后台允许运行时长配额。
-
配额机制:为了防止应用滥用保活,或者申请后不取消,每个应用每天都会有一定配额(会根据用户的使用习惯动态调整),配额消耗完就不再允许申请短时任务,所以应用完成短时任务后应立刻取消延迟挂起,避免消耗配额。(注:该配额指的是申请的时长,系统默认应用在后台运行的时间不计算在内)。
短时任务常用Api
/* * 申请短时任务 */ function requestSuspendDelay(reason: string, callback: Callback<void>): DelaySuspendInfo;
延迟挂起时间一般情况下默认值为180000,低电量(依据系统低电量广播)时默认值为60000。 注意: 具体申请时间还会根据上述的 配额机制 来决定。
/* * 获取剩余时间 */ function getRemainingDelayTime(requestId: number, callback: AsyncCallback<number>): void; function getRemainingDelayTime(requestId: number): Promise<number>;
获取应用程序进入挂起状态前的剩余时间。 注意: 这个剩余时间是当后台执行完任务之后的剩余时间,系统默认应用在后台运行的时间不计算在内。
/* * 取消短时任务 */ function cancelSuspendDelay(requestId: number): void;
取消延迟挂起。
短时任务基本用法
-
如果判断某个任务有可能在应用切到后台前还没执行完,可以在任务执行前申请短时任务。
// 开始请求短时任务 let delayInfo = backgroundTaskManager.requestSuspendDelay('请求短时任务测试', ()=>{ // 如果应用将被挂起会走此回调通知 }) // 获取短时任务id,此id唯一 var id = delayInfo.requestId; this.requestId = id // 获取短时任务申请时长 var time = delayInfo.actualDelayTime;
-
如果任务完成后可以取消此次短时任务以防止配额消耗空。
if (this.requestId > 0) { // 开始取消短时任务 backgroundTaskManager.cancelSuspendDelay(this.requestId) this.requestId = 0 // 短时任务取消完成 }
-
如果想在短时任务周期时间内处理别的任务,可以先获取剩余待挂起时间从而判断是否继续执行任务。
// 获取剩余待挂起时间 backgroundTaskManager.getRemainingDelayTime(this.requestId, (err, res)=>{ // res为剩余时间 })
长时任务
长时任务给用户能够直观感受到的且需要一直在后台运行的业务提供后台运行生命周期的保障。比如:业务需要在后台播放声音、需要在后台持续导航定位等。此类用户可以直观感知到的后台业务行为,可以通过使用长时任务对应的后台模式保障业务在后台的运行,支撑应用完成在后台的业务。
后台模式分类
OpenHarmony提供了九种后台模式,供需要在后台做长时任务的业务使用,具体的后台模式类型如下:
BackgroundMode | 说明 | 通知栏显示提示 | 备注 |
---|---|---|---|
dataTransfer | 通过网络/对端设备进行数据下载、备份、分享、传输等 | 正在运行数据传输任务 | |
audioPlayback | 音频输出 | 正在运行音频播放任务 | |
audioRecording | 音频输入 | 正在运行录音任务 | |
location | 定位、导航 | 正在运行定位任务 | |
bluetoothInteraction | 蓝牙传输 | 正在运行蓝牙相关任务 | |
multiDeviceConnection | 分布式互联任务 | 正在运行分布式任务 | |
wifiInteraction | WLAN传输 | 正在运行WLAN相关任务 | SystemApi,仅对System权限应用开放 |
voip | 音视频电话、VOIP | 正在运行通话相关任务 | SystemApi,仅对System权限应用开放 |
taskKeeping | 计算任务 | 正在运行计算任务 | 仅在特定设备生效 |
长时任务使用约束
-
如果用户选择可感知业务(如播音、导航等),触发对应后台模式,在任务启动时,系统会强制弹出通知提醒用户。
-
如果任务结束,应用应主动退出后台模式。若在后台运行期间,系统检测到应用并未使用对应后台模式的资源,则会被挂起(Suspend)。
-
避免不合理地申请后台长时任务,长时任务类型要与应用的业务类型匹配。如果执行的任务和申请的类型不匹配,也会被系统检测到并被挂起(Suspend)。
-
长时任务是为了真正在后台长时间执行某个任务,如果一个应用申请了长时任务,但在实际运行过程中,并未真正运行或执行此类任务时,也会被系统检测到并被挂起(Suspend)。
一个Ability同一时刻只能申请运行一个长时任务。
长时任务常用Api
/* * 申请长时任务 */ function startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent, callback: AsyncCallback<void>): void;
服务启动后,向系统申请长时任务,使服务一直保持后台运行。
/* * 取消长时任务 */ function stopBackgroundRunning(context: Context, callback: AsyncCallback<void>): void;
停止后台长时任务的运行。
长时任务基本用法
如果有音乐播放、数据传输、定位导航等需要长时在后台不间断运行的任务时,可以在任务执行前就向系统申请长时任务。
-
在module.json5中申请静态权限
"requestPermissions": [ { "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" } ]
-
在module.json5中配置后台模式
"abilities": [ { . . . "backgroundModes": [ "audioPlayback" ] } ]
-
申请长时任务
let wantAgentInfo = { wants: [ { bundleName: "com.example.backgroundtaskdemo", abilityName: "MainAbility" } ], operationType: wantAgent.OperationType.START_ABILITY, requestCode: 0, wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] }; // 通过wantAgent模块的getWantAgent方法获取WantAgent对象 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => { backgroundTaskManager.startBackgroundRunning(globalThis.context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj).then(() => { console.info("Operation startBackgroundRunning succeeded"); }).catch((err) => { console.error("Operation startBackgroundRunning failed Cause: " + err); }); });
-
如果想停止长时任务可以使用以下方式
backgroundTaskManager.stopBackgroundRunning(globalThis.context).then(() => { console.info(" Operation stopBackgroundRunning succeeded"); }).catch((err) => console.error(" Operation stopBackgroundRunning failed Cause: " + err); });
延迟任务
延迟任务调度给应用提供一个机制,允许应用根据系统安排,在系统空闲时执行实时性要求不高的任务,比如设备空闲时候做一次数据学习等场景。当应用申请延迟任务的时候,任务会被放入待调度队列,系统会根据当前状态,如内存、功耗、温度等统一决策最优的调度时机。同时支持任务的持久化,应用退出或者设备重启,设置的任务同样能够被触发。
延迟任务调度约束
延迟调度任务的使用需要遵从如下约束和规则:
-
超时:系统会设置超时机制,延迟任务回调只允许运行一段时间,超时之后,系统会主动停止。默认的超时限制为2分钟,对于系统应用,可以通过获取更长的执行时间(充电状态20分钟,非充电状态10分钟)。
-
执行频率:系统会根据应用的活跃度对延迟任务做分级管控,限制延迟任务调度的执行频率。对于通过能效资源接口申请了WORK_SCHEDULER资源的应用,在资源的有效期内,它的延迟任务执行频率不受限制。
应用分组 延迟任务执行频率约束 活跃 最小间隔2小时 每日使用 最小间隔4小时 经常使用 最小间隔24小时 不经常使用 最小间隔48小时 受限分组 禁止 未使用分组 禁止 执行频率不受限制 -
WorkInfo设置参数约束
-
workId、bundleName、abilityName为必填项,bundleName必须填本应用,否则校验失败。
-
至少要设置一个满足的条件。
-
重复任务时间间隔至少20分钟,当设置重复任务时间间隔时,必须设置始终重复和重复次数中的一个。
-
携带参数信息支持number、string、bool三种类型。
-
workScheduler主要接口
接口名 | 接口描述 |
---|---|
startWork(work: WorkInfo): boolean; | 延迟调度任务申请 |
stopWork(work: WorkInfo, needCancel?: boolean): boolean; | 延迟调度任务取消 |
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(): boolean; | 停止并清除任务 |
isLastWorkTimeOut(workId: number, callback: AsyncCallback<void>): boolean; | 获取上次任务是否超时(针对RepeatWork,Callback形式) |
isLastWorkTimeOut(workId: number): Promise<boolean>; | 获取上次任务是否超时(针对RepeatWork,Promise形式) |
WorkInfo包含参数
说明: WorkInfo设置参数约束见
注:workInfo设置触发条件,条件生效触发延迟任务
参数名 | 类型 | 描述 |
---|---|---|
workId | number | 延迟任务Id(必填) |
bundleName | string | 延迟任务包名(必填) |
abilityName | string | 延迟任务回调通知的组件名(必填) |
networkType | 网络类型 | |
isCharging | boolean | 是否充电 |
chargerType | 充电类型 | |
batteryLevel | number | 电量 |
batteryStatus | 电池状态 | |
storageRequest | 存储状态 | |
isRepeat | boolean | 是否循环任务 |
repeatCycleTime | number | 循环间隔 |
repeatCount | number | 循环次数 |
parameters | {[key: string]: any} | 携带参数信息 |
延迟任务回调接口
注:延迟任务具体执行逻辑
接口名 | 接口描述 |
---|---|
onWorkStart(work: WorkInfo): void | 延迟调度任务开始回调 |
onWorkStop(work: WorkInfo): void | 延迟调度任务结束回调 |
延迟任务基本用法
1.开发对应的ExtensionAbility,用于回调执行具体的延迟任务。关于ExtensionAbility的介绍,参考。
import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility'; const TAG = "backgroundTaskDemo:"; export default class MyExtension extends WorkSchedulerExtensionAbility { // 延迟任务满足条件后执行的任务体 onWorkStart(workInfo) { console.info(TAG + 'MyWorkSchedulerExtensionAbility onWorkStart' + JSON.stringify(workInfo)); var count = 0; let intervalId = setInterval(() => { if (count >= 100) { clearInterval(intervalId); } console.info(TAG + 'MyWork is running!') count++; }, 1000); } // 延迟任务取消后执行 onWorkStop(workInfo) { console.info(TAG + 'MyWorkSchedulerExtensionAbility onWorkStop' + JSON.stringify(workInfo)); } }
2.在module.json5中配置extensionAbilities
"extensionAbilities": [ { "name": "MyExtension", "icon": "$media:icon", "description": "MyExtension", "type": "workScheduler", "visible": true, "srcEntrance": "./ets/service/MyExtension.ts" } ]
注:这里的type一定要是workScheduler,否则任务无法调用
3.注册延迟任务
let workInfo = { workId: 1, networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI, isRepeat: false, isPersisted: true, bundleName: "com.example.backgroundtaskdemo", abilityName: "MyExtension", parameters: { mykey0: 1, mykey1: "string value", mykey2: true, mykey3: 1.5 } } var res = workScheduler.startWork(workInfo); console.info(TAG + "startWork res:" + res);
4.取消延迟任务
let workInfo = { workId: 1, networkType: workScheduler.NetworkType.NETWORK_TYPE_WIFI, isRepeat: false, isPersisted: true, bundleName: "com.example.backgroundtaskdemo", abilityName: "MyExtension", parameters: { mykey0: 1, mykey1: "string value", mykey2: true, mykey3: 1.5 } } var res = workScheduler.stopWork(workInfo, false); console.info("stopWork res:" + res);
5.停止并清除任务
let res = workScheduler.stopAndClearWorks(); console.info("workschedulerLog res:" + res);
申请能效资源
能效资源可以分为四种:CPU资源,WORK_SCHEDULER资源,软件资源(COMMON_EVENT,TIMER),硬件资源(GPS,BLUETOOTH,AUDIO)。
应用或进程申请能效资源后能够获得相应特权:
-
申请CPU资源后可以不被挂起,直到任务完成。
-
申请WORK_SCHEDULER资源后不受延迟任务执行频率约束,且任务执行时间增加。
-
申请COMMON_EVENT资源后,应用在后台处于挂起状态时,仍然能够接收到系统公共事件,申请TIMER资源后,应用能够使用定时器执行精确定时任务。
-
申请硬件资源后,应用在后台被挂起后,依然能够被相关服务唤醒,执行相应的任务。
能效资源种类
参数名 | 参数值 | 描述 |
---|---|---|
CPU | 1 | CPU资源,申请后不被挂起 |
COMMON_EVENT | 2 | 公共事件,申请后挂起状态下不被代理掉 |
TIMER | 4 | 计时器,申请后挂起状态下不被代理掉 |
WORK_SCHEDULER | 8 | 延迟任务,申请后有更长的执行时间 |
BLUETOOTH | 16 | 蓝牙相关,申请后挂起状态下不被代理掉 |
GPS | 32 | GPS相关,申请后挂起状态下不被代理掉 |
AUDIO | 64 | 音频资源,申请后挂起状态下不被代理掉 |
能效资源使用约束
-
能效资源申请或者释放可以由进程或者应用发起,由应用发起的资源释放会释放属于它的同类型的所有资源,包括进程申请的资源。例如应用申请了CPU资源,进程申请了CPU和WORK_SCHEDULER资源,当应用释放CPU资源的时候,会将进程的CPU资源一同释放,同时不同类型的WORK_SCHEDULER资源不受影响。由进程发起的资源释放对应用申请的资源没有影响,例如当应用和进程同时申请了CPU,进程发起了CPU资源释放,应用的CPU资源不会被释放。
-
同时申请同一类持久资源和非持久资源,持久资源会覆盖非持久资源,在超时时不会释放资源。例如应用首先申请了10s的CPU资源,然后在第5s的时候申请了持久的CPU资源,那么资源会变成持久的,非持久的CPU资源记录会被持久化的CPU资源记录覆盖,到了第10s的时候资源不会被释放,如果在第8s的时候提前释放了资源,那么会将CPU资源释放,无法单独释放其中非持久的或者持久的CPU资源。
-
WORK_SCHEDULER资源只能由应用申请和释放,不能由进程申请和释放。
-
需要使用能效资源的应用,需要向应用中心提出申请,获取相应的特权。
注:能效资源申请或者释放接口暂未正式对外开放
示例演示
版本:wgr3.2.7.5
须知:
-
rk目前不支持应用挂起,应用后置,音乐正常播放;所以该模块功能目前只适用于wgr设备
-
AVSession服务增加了三方应用后台管控策略,只有三方应用接入了AVSession,才可以后台播放,否则当应用切后台时系统会强制暂停其音频播放
-
api接口'@ohos.multimedia.avsession'暂未对外开放,需等待后续更新
-
申请能效资源接口暂未正式对外开放
1.短时任务
初始情况:
点击申请短时任务按钮,申请短时任务,当剩余时间大于0时,开启计时器,每秒计数一次
点击Home键,应用后台运行,这时短时任务开始执行,剩余时间开始消耗,一段时间后,前置应用
应用前置后,短时任务暂停
短时任务时间耗完,任务自动终止,或通过backgroundTaskManager.cancelSuspendDelay取消
2.长时任务
点击申请长时任务,应用循环播放音乐,应用后置,任务后台运行,音乐继续播放
注:AVSession服务增加了三方应用后台管控策略,只有三方应用接入了AVSession,才可以后台播放,否则当应用切后台时系统会强制暂停其音频播放
由于api接口'@ohos.multimedia.avsession'暂未对外开放,所以需等待后续更新
通过backgroundTaskManager.stopBackgroundRunning终止任务
3.延迟任务
点击注册延迟任务,注册连接wifi触发的延迟任务
查看日志显示'startWork res:true',任务成功注册
通过命令'hidumper -s 1904 -a '-d network wifi''触发连接wifi,或通过设备连接wifi触发
触发后,任务正常执行,每秒输出日志'MyWork is running!',输出100次结束。
点击取消延迟任务,任务终止。
4.申请能效资源
接口暂未正式对外开放。
Worker(有可见界面)
Worker是与主线程并行的独立线程。创建Worker的线程称之为宿主线程,Worker自身的线程称之为Worker线程。创建Worker传入的url文件在Worker线程中执行,可以处理耗时操作但不可以直接操作UI。
API接口
constructor
注:构造方法,创建Worker实例
constructor(scriptURL: string, options?: WorkerOptions)
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
scriptURL | string | 是 | Worker执行脚本的路径。 在FA和Stage模型下,DevEco Studio新建Worker工程路径分别存在以下两种情况: (a) worker脚本所在目录与pages目录同级。 (b) worker脚本所在目录与pages目录不同级。 |
options | 否 | Worker构造的选项。 |
示例:
import worker from '@ohos.worker'; // worker线程创建 // FA模型-目录同级 const workerFAModel01 = new worker.Worker("workers/worker.js", {name:"first worker in FA model"}); // FA模型-目录不同级(以workers目录放置pages目录前一级为例) const workerFAModel02 = new worker.Worker("../workers/worker.js"); // Stage模型-目录同级 const workerStageModel01 = new worker.Worker('entry/ets/workers/worker.ts', {name:"first worker in Stage model"}); // Stage模型-目录不同级(以workers目录放置pages目录后一级为例) const workerStageModel02 = new worker.Worker('entry/ets/pages/workers/worker.ts'); // 理解Stage模型scriptURL的"entry/ets/workers/worker.ts": // entry: 为module.json5文件中module的name属性对应的值; // ets: 表明当前使用的语言。
postMessage
注:向Worker线程发送数据,数据类型必须是序列化所支持的类型。序列化支持类型见其他说明。
postMessage(message: Object, options?: PostMessageOptions): void
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
message | Object | 是 | 发送至Worker的数据。 |
options | 否 | 可转移对象是 ArrayBuffer 的实例对象。transferList数组中不可传入null。 |
示例:
const workerInstance = new worker.Worker("workers/worker.js"); workerInstance.postMessage("hello world"); var buffer = new ArrayBuffer(8); workerInstance.postMessage(buffer, [buffer]);
on
注:向Worker添加一个事件监听
on(type: string, listener: EventListener): void
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
type | string | 是 | 监听事件的type。 |
listener | 是 | 回调的事件。 |
示例:
const workerInstance = new worker.Worker("workers/worker.js") workerInstance.on("alert", (e)=>{ console.log("alert listener callback"); })
once
注:向Worker添加一个事件监听,事件监听只执行一次便自动删除
once(type: string, listener: EventListener): void
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
type | string | 是 | 监听事件的type。 |
listener | 是 | 回调的事件。 |
示例:
const workerInstance = new worker.Worker("workers/worker.js"); workerInstance.once("alert", (e)=>{ console.log("alert listener callback"); })
off
注:删除Worker的事件监听
off(type: string, listener?: EventListener): void
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
type | string | 是 | 需要删除事件的type。 |
listener | 否 | 需要删除的回调的事件。 |
示例:
const workerInstance = new worker.Worker("workers/worker.js"); workerInstance.off("alert");
terminate
注:销毁Worker线程,终止Worker接收消息。
terminate(): void
示例:
const workerInstance = new worker.Worker("workers/worker.js") workerInstance.terminate()
onexit
注:Worker对象的onexit属性表示Worker销毁时被调用的事件处理程序,处理程序在宿主线程中执行。
onexit?: (code: number) => void
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
code | number | 否 | Worker退出的code。 |
示例:
const workerInstance = new worker.Worker("workers/worker.js") workerInstance.onexit = function(e) { console.log("onexit") }
onerror
注:Worker对象的onerror属性表示Worker在执行过程中发生异常被调用的事件处理程序,处理程序在宿主线程中执行
onerror?: (err: ErrorEvent) => void
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
err | 否 | 异常数据。 |
示例:
const workerInstance = new worker.Worker("workers/worker.js") workerInstance.onerror = function(e) { console.log("onerror") }
onmessage
注:Worker对象的onmessage属性表示宿主线程接收到来自其创建的Worker通过parentPort.postMessage接口发送的消息时被调用的事件处理程序,处理程序在宿主线程中执行
onmessage?: (event: MessageEvent) => void
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
event | 否 | 收到的Worker消息数据。 |
示例:
const workerInstance = new worker.Worker("workers/worker.js"); workerInstance.onmessage = function(e) { // e : MessageEvent<T>, 用法如下: // let data = e.data; console.log("onmessage"); }
onmessageerror
注:Worker对象的onmessageerror属性表示当Worker对象接收到一条无法被序列化的消息时被调用的事件处理程序,处理程序在宿主线程中执行
onmessageerror?: (event: MessageEvent) => void
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
event | 否 | 异常数据。 |
示例:
const workerInstance = new worker.Worker("workers/worker.js"); workerInstance.onmessageerror= function(e) { console.log("onmessageerror"); }
使用示例
1.新建工程
通过DevEco Studio 3.0.0.993新建一个OpenHarmony的工程,Project Type为Application,SDK版本为api9,Model为Stage,Language为eTS,Device type全部勾选。
2.创建Worker线程
导入模块
import worker from '@ohos.worker';
声明示例:
// worker线程创建 const workerInstance = new worker.Worker('entry/ets/workers/worker.ts', { name: "worker of Stage model" });
注:FA模型和Stage模型的声明路径不同,这里的示例为Stage模型
3.发送消息
调用postMessage向Worker线程发送数据,数据类型必须是序列化所支持的类型。
示例:
function postMessage(data) { console.info(TAG + "postMessage"); workerInstance.postMessage(data); }
4.执行调用
Worker线程接收到主线程消息后,可根据具体信息执行相关的逻辑调用
示例:
const parentPort = worker.parentPort; parentPort.onmessage = function (e) { console.info(TAG + "receive data from main, data:" + e.data); if (e.data == 'OK') { parentPort.postMessage('running'); doProcess(); } } /** * 需要执行的耗时操作 */ async function doProcess() { var result = ''; try { console.info(TAG + 'doProcess beginning!'); for (var i = 0; i < 100; i++) { console.info(TAG + i); } console.info(TAG + 'doProcess success!'); result = 'success'; } catch (e) { console.error(TAG + 'doProcess failed:' + JSON.stringify(e)); result = 'doProcess failed:' + JSON.stringify(e); } finally { setTimeout(() => { parentPort.postMessage(result) }, 3000); } }
见源码:Worker.ts
注:worker线程可以给主线程发多次消息,但是要注意发送消息的写法
5.返回消息
Worker执行完成操作后,将执行结果发送给主线程
const parentPort = worker.parentPort; parentPort.postMessage(result);
6.调用结果渲染
主线程接收到执行结果信息后,根据结果进行渲染
注:Worker线程不可以直接操作UI,@State变量无法直接进行赋值渲染,所以需要通过其他方式进行传值
示例:
let workerResult = AppStorage.Link('workerResult'); @StorageLink('workerResult') @Watch("resultChange") workerResult: string = ""; resultChange() { this.result = AppStorage.Get("workerResult"); } workerInstance.onmessage = function (e) { console.info(TAG + "receive data from worker, data:" + e.data); // 注:worker中不可以直接操作UI,所以需要借用AppStorage来进行传值 AppStorage.Set<string>('workerResult', e.data); }
注:这里通过AppStorage进行传值,具体源码见index.ets
基于以上条件即可实现Worker的简单调用了,示例功能包含:主线程与Worker之间的通信、Worker逻辑处理及主线程处理结果渲染等。
7.添加配置
需在工程的模块级build-profile.json5文件的buildOption属性中添加配置信息,主要分为下面两种情况:
// 目录同级(**此情况不添加亦可**) // FA模型: "buildOption": { "sourceOption": { "workers": [ "./src/main/ets/MainAbility/workers/worker.js" ] } } // Stage模型: "buildOption": { "sourceOption": { "workers": [ "./src/main/ets/workers/worker.ts" ] } } // 目录不同级(**此情况必须添加**) // FA模型: "buildOption": { "sourceOption": { "workers": [ "./src/main/ets/workers/worker.js" ] } } // Stage模型(workers目录放在pages目录里为例): "buildOption": { "sourceOption": { "workers": [ "./src/main/ets/pages/workers/worker.ts" ] } }
8.示例演示
注:系统版本:3.2.8.2
其他场景
worker处理异常
处理异常的接口有worker.onerror和parentPort.onerror方法。注意,当发生异常后worker实例会被销毁。 worker线程在执行过程中发生异常会触发onerror方法,注意,worker.onerror方法执行在宿主线程,parentPort.onerror执行在worker线程。
// 宿主线程 worker.onerror = function(e) { var message = e.message; // 异常信息 var filename = e.filename; // 异常所在的文件 var linno = e.lineno; // 异常出现的行号 var colno = e.colno; // 异常出现的列号 }
// worker线程 parentPort.onerror = function(e) { var message = e.message; // 异常信息 var filename = e.filename; // 异常所在的文件 var linno = e.lineno; // 异常出现的行号 var colno = e.colno; // 异常出现的列号 }
取消任务
取消任务的接口有worker.cancelTasks()和parentPort.cancelTasks(); worker支持取消发送的任务,正在执行的任务不可取消。 worker取消任务的方式有两种,第一种在宿主线程调用worker.cancelTasks();第二种在worker线程调用parentPort.cancelTasks()。
销毁worker
销毁worker的方式有两种;
-
被动销毁 worker线程的生命周期跟随应用。若应用退出则释放worker资源。 worker线程在执行过程中出现异常终止掉worker。
-
主动销毁 主动销毁worker的接口有worker.terminate()和parentPort.close(); 主动销毁worker的方式有两种,第一种在宿主线程调用worker.terminate();第二种在worker线程调用parentPort.close()。 worker销毁前会触发onexit回调,注意,onexit回调只会执行在宿主线程。
// 宿主线程 worker.onexit = function() { // 自己的销毁逻辑 } worker.terminate();
// worker线程 parentPort.close();
参考文献
[1] OpenHarmony开发者文档-后台任务管理.
[2] OpenHarmony开发者文档-后台任务.
[3]OpenHarmony开发者文档-媒体会话管理.
[4] OpenHarmony开发者文档-启动一个Worker.
更多推荐
所有评论(0)