《解压Zip文件》二、启动一个Worker指南

📅 2026/7/23 0:41:23 👁️ 阅读次数 📝 编程学习
《解压Zip文件》二、启动一个Worker指南

HarmonyOS @ohos.worker (启动一个Worker) 使用指南:多线程实战详解

模块类型:系统内置模块
关键词:Worker、多线程、后台任务、线程通信、ArkTS


效果

一、为什么需要 Worker?

在 HarmonyOS 应用中,主线程(UI 线程)负责界面渲染和用户交互。当执行耗时操作(如大文件解压、复杂计算、数据解析)时,主线程会被阻塞,导致界面卡顿甚至 ANR(应用无响应)。

@ohos.worker模块提供了多线程能力,允许开发者将耗时任务放到独立的 Worker 线程中执行,主线程保持流畅。

工作原理图

┌─────────────────┐ postMessage() ┌─────────────────┐ │ │ ─────────────────────► │ │ │ 主线程 (UI) │ │ Worker 线程 │ │ │ ◄───────────────────── │ (后台执行) │ └─────────────────┘ onmessage └─────────────────┘ │ 负责:UI 渲染 │ 负责:耗时任务 │ 用户交互 │ 文件操作 │ 事件处理 │ 数据计算

线程通信模型

Worker 线程与主线程之间通过消息机制进行通信:

  • 主线程通过postMessage()向 Worker 发送数据
  • Worker 线程通过workerPort.postMessage()向主线程返回结果
  • 双方通过onmessage回调接收对方的消息

二、核心 API 一览

2.1 主线程侧 API

importworker,{MessageEvents}from'@ohos.worker';
方法/属性说明
new worker.ThreadWorker(scriptPath)创建 Worker 线程实例
worker.postMessage(data)向 Worker 线程发送消息
worker.onmessage接收 Worker 线程返回的消息
worker.onmessageerror接收反序列化失败的消息
worker.onerror接收 Worker 线程异常事件
worker.terminate()销毁 Worker 线程,释放资源

2.2 Worker 线程侧 API

import{MessageEvents,ErrorEvent,ThreadWorkerGlobalScope,worker}from'@kit.ArkTS';
方法/属性说明
worker.workerPort获取 Worker 线程的全局作用域
workerPort.postMessage(data)向主线程发送消息
workerPort.onmessage接收主线程发来的消息
workerPort.onmessageerror接收反序列化失败的消息
workerPort.onerror接收 Worker 运行异常
workerPort.close()关闭 Worker 线程(线程内部调用)

三、完整实战示例

3.1 场景:后台执行文件解压

本示例演示一个完整的 Worker 使用流程:主线程发起解压请求,Worker 在后台执行解压,完成后通知主线程。

3.2 步骤一:配置 Worker 入口

entry/build-profile.json5中声明 Worker 文件路径:

{ "apiType": "stageMode", "buildOption": { "sourceOption": { "workers": [ "./src/main/ets/workers/UnzipWorker.ets" ] } } }

重要:必须在build-profile.json5中注册 Worker 文件,否则编译时不会打包该文件,运行时会报错找不到脚本。

3.3 步骤二:编写 Worker 线程代码

文件路径:entry/src/main/ets/workers/UnzipWorker.ets

import{ErrorEvent,MessageEvents,ThreadWorkerGlobalScope,worker}from'@kit.ArkTS';importzlibfrom'@ohos.zlib';import{BusinessError}from'@ohos.base';import{hilog}from'@kit.PerformanceAnalysisKit';constTAG='UnzipWorker';// ✅ 定义消息协议接口,避免 any 类型错误interfaceWorkerRequest{zipPath:string;extractPath:string;taskId:number;}// 获取 Worker 线程的全局通信端口constworkerPort:ThreadWorkerGlobalScope=worker.workerPort;// 监听主线程发来的消息workerPort.onmessage=(e:MessageEvents)=>{// ✅ 必须显式类型转换,ArkTS 禁止使用隐式 anyconstdata:WorkerRequest=e.dataasWorkerRequest;constzipPath:string=data.zipPath;constextractPath:string=data.extractPath;consttaskId:number=data.taskId;hilog.info(0x0001,TAG,`收到解压任务:${zipPath}`);constoptions:zlib.Options={level:zlib.CompressLevel.COMPRESS_LEVEL_DEFAULT_COMPRESSION};try{zlib.decompressFile(zipPath,extractPath,options,(err:BusinessError)=>{if(err!==null&&err.code!==0){// 解压失败,通知主线程workerPort.postMessage({taskId:taskId,status:'error',message:`解压失败:${err.message}`});}else{// 解压成功,通知主线程workerPort.postMessage({taskId:taskId,status:'success',message:'解压完成'});}});}catch(err){constbizErr=errasBusinessError;workerPort.postMessage({taskId:taskId,status:'error',message:`异常:${bizErr.message}`});}};// 监听消息反序列化错误workerPort.onmessageerror=(e:MessageEvents)=>{hilog.error(0x0001,TAG,'消息反序列化失败');};// 监听 Worker 运行异常workerPort.onerror=(e:ErrorEvent)=>{hilog.error(0x0001,TAG,`Worker 异常:${e.message}`);};

3.4 步骤三:在主线程中使用 Worker

importworker,{MessageEvents}from'@ohos.worker';import{PromptAction}from'@kit.ArkUI';import{Context}from'@kit.AbilityKit';interfaceUnzipResult{taskId:number;status:'success'|'error';message:string;}functionstartUnzipInWorker(context:Context,zipPath:string,extractPath:string,promptAction:PromptAction):void{// 1. 创建 Worker 实例(路径相对于 entry/src/main/ets/)constunzipWorker=newworker.ThreadWorker('entry/ets/workers/UnzipWorker.ets');// 2. 监听 Worker 返回的消息unzipWorker.onmessage=(e:MessageEvents):void=>{constresult=e.dataasUnzipResult;if(result.status==='success'){promptAction.showToast({message:result.message});}else{promptAction.showToast({message:result.message});}// 3. 使用完毕后销毁 Worker,释放资源unzipWorker.terminate();};// 3. 监听 Worker 异常unzipWorker.onerror=(e:ErrorEvent):void=>{promptAction.showToast({message:`Worker 错误:${e.message}`});unzipWorker.terminate();};// 4. 向 Worker 发送解压任务unzipWorker.postMessage({zipPath:zipPath,extractPath:extractPath,taskId:Date.now()});}

四、消息传递的数据类型

Worker 线程与主线程之间传递的数据通过结构化克隆算法进行序列化,支持以下类型:

支持的类型示例
基本类型stringnumberbooleannullundefined
对象与数组{ name: 'test' }[1, 2, 3]
ArrayBuffer二进制数据传输
Map / Set集合类型
Date / RegExp内置对象

不支持的类型(会抛出序列化异常):

  • 函数(Function)
  • DOM 节点
  • Symbol
  • WeakMap / WeakSet
  • 包含循环引用的对象

五、Worker 生命周期管理

5.1 创建阶段

constmyWorker=newworker.ThreadWorker('entry/ets/workers/MyWorker.ets');

创建时传入的路径格式为:{moduleName}/ets/workers/{fileName}.ets

5.2 运行阶段

// 发送消息myWorker.postMessage({action:'start',payload:data});// 接收消息myWorker.onmessage=(e:MessageEvents)=>{/* 处理结果 */};

5.3 销毁阶段

// 主线程销毁 WorkermyWorker.terminate();// Worker 线程自行关闭(在 Worker 内部调用)workerPort.close();

最佳实践:Worker 使用完毕后务必调用terminate()销毁,否则线程会持续占用系统资源。


六、最佳实践

6.1 何时使用 Worker

适合使用 Worker不适合使用 Worker
文件压缩/解压简单的数据赋值
大文件读写UI 状态更新
图片处理/编码路由导航
复杂数据计算快速同步操作
JSON 大文件解析事件监听注册

6.2 错误处理规范

// ✅ 推荐:完整的错误处理链myWorker.onmessage=(e:MessageEvents)=>{constresult=e.dataasTaskResult;if(result.status==='error'){// 处理业务错误}myWorker.terminate();// 确保销毁};myWorker.onerror=(e:ErrorEvent)=>{// 处理线程异常myWorker.terminate();};

6.3 多 Worker 管理

如果需要同时处理多个任务,可以创建多个 Worker 实例:

// 并行解压多个文件for(constzipFileofzipFiles){constw=newworker.ThreadWorker('entry/ets/workers/UnzipWorker.ets');w.onmessage=(e:MessageEvents)=>{// 处理各自的结果w.terminate();};w.postMessage({zipPath:zipFile.path,extractPath:zipFile.out});}

七、常见问题

Q1:Worker 文件找不到?

确保已在build-profile.json5sourceOption.workers中注册路径。

Q2:postMessage 报错 “DataCloneError”?

检查传递的数据是否包含不支持的类型(如函数、Symbol 等)。

Q3:Worker 中能否访问 UI?

不能。Worker 线程无法操作 UI 组件,只能通过消息将结果传回主线程,由主线程更新 UI。

Q4:Worker 中能否使用 async/await?

可以。Worker 线程支持异步操作,但需注意 Worker 的生命周期管理。

Q5:报错 “Use explicit types instead of any, unknown” (arkts-no-any-unknown)?

这是 ArkTS 的严格类型检查规则。MessageEventsdata属性返回类型为隐式any,在 ArkTS 中必须显式声明类型:

// ❌ 错误:隐式 anyworkerPort.onmessage=(e:MessageEvents)=>{constdata=e.data;// 编译报错 arkts-no-any-unknownconstpath=data.zipPath;};// ✅ 正确:定义接口 + 显式类型转换interfaceWorkerRequest{zipPath:string;extractPath:string;}workerPort.onmessage=(e:MessageEvents)=>{constdata:WorkerRequest=e.dataasWorkerRequest;// 显式类型constpath:string=data.zipPath;};

经验提示:主线程接收 Worker 消息时同样需要显式类型转换,如const resp = e.data as WorkerResponse;