解锁HarmonyOS进程与线程的神秘面纱

目录

一、进程与线程:基础概念

二、HarmonyOS 进程探秘

2.1 进程类型解析

2.2 进程创建规则

2.3 跨进程通信方式

三、HarmonyOS 线程剖析

3.1 线程类型一览

3.2 线程创建之道

3.3 线程间通信机制

四、实际开发运用

4.1 场景示例

4.2 代码示例

五、总结与展望


一、进程与线程:基础概念

        在深入探索 HarmonyOS 的进程与线程之前,让我们先回顾一下它们的基本概念。进程可以被看作是正在运行的程序的实例,是操作系统进行资源分配的基本单位。当你打开一个 HarmonyOS 应用,系统就会为其创建一个对应的进程,并分配内存、文件描述符等资源 。每个进程都有自己独立的地址空间,这意味着不同进程之间的内存是相互隔离的,一个进程无法直接访问另一个进程的内存,从而保证了系统的稳定性和安全性。例如,当你同时打开音乐播放器和浏览器,它们分别运行在各自的进程中,互不干扰。即使音乐播放器进程出现异常崩溃,也不会影响浏览器的正常运行。

        而线程则是进程中的一个执行单元,是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等,但每个线程有自己独立的栈空间和程序计数器,用于存储局部变量和记录线程的执行位置。以浏览器进程为例,它可能包含负责页面渲染的线程、处理用户输入事件的线程以及进行网络请求的线程等。这些线程协同工作,使得浏览器能够同时处理多个任务,提供流畅的用户体验。

        在 HarmonyOS 系统中,进程与线程扮演着至关重要的角色。它们是系统实现多任务处理、并发执行的基础,直接影响着应用的性能和用户体验。合理地管理和使用进程与线程,可以充分利用系统资源,提高应用的响应速度和执行效率,为用户带来更加流畅和高效的操作体验。

二、HarmonyOS 进程探秘

2.1 进程类型解析

        在 HarmonyOS 中,一个应用通常会涉及以下几类进程 :

主进程:它是应用运行的核心进程,承载着应用的主要业务逻辑和用户界面展示。应用中的所有 UIAbility(即应用的界面部分)以及一些默认的扩展能力组件,如 ServiceExtensionAbility 和 DataShareExtensionAbility,都运行在主进程中。以一个社交类应用为例,用户在使用过程中看到的聊天界面、好友列表界面等,都是在主进程中进行渲染和交互处理的。主进程就像是应用的 “大脑”,协调着各个功能模块的运行,对用户的操作做出及时响应。

ExtensionAbility 进程:这是为了满足特定场景扩展需求而设立的进程。同一种类型的 ExtensionAbility 会被放置在同一个进程里,例如,多个 FormExtensionAbility 会在同一个进程中运行。这些扩展能力组件可以为应用提供多样化的功能扩展,如服务卡片、输入法等功能。以服务卡片为例,它可以在设备的负一屏等位置展示应用的关键信息或快捷操作入口,而承载这些服务卡片逻辑的 ExtensionAbility 就运行在对应的 ExtensionAbility 进程中,与主进程相互配合,为用户提供更加便捷和丰富的使用体验 。

WebView 渲染进程:专门用于网页容器渲染。当应用中需要加载网页内容时,WebView 渲染进程就会发挥作用。它拥有独立的进程空间,这使得网页的渲染过程与应用的其他部分隔离开来,大大提高了安全性和稳定性。比如,当应用内嵌了一个网页版的新闻详情页面,WebView 渲染进程会负责解析和渲染该网页,即使网页中存在一些潜在的恶意代码或不稳定因素,也不会轻易影响到应用的主进程,从而保障了应用的正常运行和用户数据的安全 。这种设计有效地提升了鸿蒙系统中 arkWeb 的网页容器渲染能力,为用户带来更流畅、更安全的网页浏览体验。

2.2 进程创建规则

        对于三方应用而言,创建进程的方式主要是通过创建 XXExtensionAbility 来实现。例如,开发者可以根据业务需求创建自定义的 ExtensionAbility,并为其指定运行的进程,以此来增加应用内的进程数量,实现功能的模块化和并行处理。然而,需要注意的是,WebView 渲染进程和主进程是不能随意创建增加的。主进程作为应用的核心运行环境,其创建和管理由系统严格控制,以确保应用的稳定性和兼容性;WebView 渲染进程则是为了保证网页渲染的安全性和独立性,同样不允许三方应用随意创建,而是由系统在需要加载网页时自动创建和管理。这种进程创建规则有助于维持系统的稳定运行,避免因进程的无序创建而导致资源浪费和系统性能下降。

2.3 跨进程通信方式

        在 HarmonyOS 中,为了实现不同进程之间的信息交互和协同工作,提供了以下几种跨进程通信方式 :

公共事件:公共事件是一种广泛应用的跨进程通信机制,它主要用于监听系统事件以及实现应用之间或应用内不同进程之间的事件通知。公共事件可以分为系统公共事件和自定义事件。系统公共事件是由系统内部定义和触发的,例如应用包安装完成、设备屏幕点亮或熄灭、设备关机等事件。这些系统公共事件为应用提供了与系统状态进行交互的机会,应用可以通过订阅这些事件来做出相应的响应。例如,一个音乐播放应用可以订阅设备屏幕熄灭事件,当屏幕熄灭时自动暂停播放,以节省电量;当屏幕点亮时再恢复播放,为用户提供无缝的音乐播放体验。

        而对于三方应用来说,自定义事件则是更为常用的通信方式。应用可以根据自身的业务逻辑定义各种自定义事件,并在需要的时候发布这些事件。其他进程或应用通过订阅这些自定义事件,就能够接收到相关的通知,并执行相应的处理逻辑。比如,在一个电商应用中,当用户完成订单支付后,支付模块所在的进程可以发布一个 “订单支付成功” 的自定义事件,订单管理模块所在的进程通过订阅该事件,及时更新订单状态,为用户展示支付成功后的订单详情和后续操作提示,实现了不同模块之间的高效协作 。

Emitter:Emitter 是另一种实现跨进程通信的方式,它类似于一个事件发射器,主要用于在不同线程或进程之间传递事件。Emitter 在 EventHub 的基础上进行了扩展,增加了传递包裹的封装,同时还支持设置事件的优先级和序号,并且提供了 once 接口,用于监听一次特定事件。例如,在一个多进程的文件处理应用中,一个进程负责读取文件内容,另一个进程负责对文件内容进行处理。读取文件的进程可以通过 Emitter 发送一个 “文件读取完成” 的事件,并携带读取到的文件内容作为事件数据;处理文件的进程通过监听该事件,获取文件内容并进行相应的处理。通过 Emitter,不同进程之间可以实现高效的数据共享和事件同步,确保各个模块之间的协同工作 。

三、HarmonyOS 线程剖析

3.1 线程类型一览

        在 HarmonyOS 的线程体系中,主要包含以下几种类型的线程 :

主线程:作为应用的核心线程,主线程肩负着众多关键职责。它负责执行 UI 绘制工作,从界面的布局计算到元素的渲染,再到屏幕的实时刷新,都离不开主线程的高效运作。例如,当用户打开一个购物应用,商品列表页面的展示、各类按钮和图标等元素的绘制,都是由主线程完成的。同时,主线程还管理着主线程的 ArkTS 引擎实例,确保多个 UIAbility 组件能够在其之上稳定运行,实现界面的流畅交互 。此外,主线程承担着分发交互事件的重任,当用户进行触摸屏幕、点击按钮、滑动屏幕等操作时,主线程会及时捕捉这些事件,并将其分发给相应的组件进行处理 。它还负责处理应用代码的回调,包括各种事件处理和生命周期管理,以及接收来自其他线程(如 Worker 线程)发送的消息,是整个应用运行的 “总指挥”。

TaskPool Worker 线程:TaskPool Worker 线程是专门用于处理轻量级耗时任务的线程。它依托于任务池(TaskPool)机制,系统会自动管理线程的创建、复用和销毁,开发者无需过多关注线程的生命周期。这一特性使得它在处理一些频繁但耗时较短的任务时表现出色,比如数据的简单计算、小文件的读写操作等。例如,在一个图片编辑应用中,对图片进行简单的滤镜调整、尺寸缩放等操作可以使用 TaskPool Worker 线程来完成,这些操作虽然需要一定的计算资源,但执行时间相对较短,使用 TaskPool Worker 线程既能保证任务的高效执行,又能避免创建过多线程带来的资源开销 。

Worker 线程:Worker 线程主要用于执行偏重的耗时任务,如网络请求、复杂的文件读写、大数据量的计算等。它与主线程相互独立,拥有自己独立的执行环境和上下文。在一个音乐下载应用中,当用户点击下载歌曲时,下载任务可以交给 Worker 线程来处理,这样就不会阻塞主线程,用户仍然可以在主线程中进行其他操作,如切换歌曲、调整音量等,从而保证了应用的流畅性和响应性。不过,需要注意的是,Worker 线程不能直接操作 UI,如果需要更新 UI,必须通过与主线程通信的方式来实现 。

3.2 线程创建之道

        在 HarmonyOS 中,创建线程有着明确的方式和规则 :

TaskPool Worker 线程创建:创建 TaskPool Worker 线程时,开发者需要使用 @Concurrent 装饰器标注要在任务池中执行的函数。该装饰器从 API version 9 开始支持,仅在 Stage 模型的工程中的.ets 文件中使用。被装饰的函数可以是 async 函数或普通函数,但禁止是 generator、箭头函数、类方法,也不支持类成员函数或者匿名函数 。在需要执行并发任务的地方,创建 taskpool.Task 对象,将定义好的任务函数作为参数传入,并通过 taskpool.execute 方法执行任务。例如:

import { taskpool } from '@kit.ArkTS';

@Concurrent

function add(num1: number, num2: number): number {

return num1 + num2;

}

async function ConcurrentFunc(): Promise<void> {

try {

let task: taskpool.Task = new taskpool.Task(add, 1, 2);

console.info("taskpool res is: " + await taskpool.execute(task));

} catch (e) {

console.error("taskpool execute error is: " + e);

}

}

        在使用 TaskPool Worker 线程时,需要注意任务函数在 TaskPool 工作线程的执行耗时不能超过 3 分钟(不包含 Promise 和 async/await 异步调用的耗时,例如网络下载、文件读写等 I/O 任务的耗时),否则会被强制退出 。同时,实现任务的函数入参需满足序列化支持的类型,ArrayBuffer 参数在 TaskPool 中默认转移,需要设置转移列表的话可通过接口 setTransferList () 设置。从 API version 11 开始,跨并发实例传递带方法的实例对象时,该类必须使用 @Sendable 装饰器标注,且仅支持在.ets 文件中使用 。

Worker 线程创建:Worker 线程的创建相对复杂一些。首先,Worker 线程文件需要放在 {moduleName}/src/main/ets/ 目录层级之下,可以手动创建相关目录及文件,并配置 build-profile.json5 的相关字段信息;也可以使用 DevEco Studio 一键生成 Worker,自动生成模板文件及配置信息 。在 API 9 及之后版本的 Stage 模型下,构造 Worker 实例时,使用 worker.ThreadWorker 构造函数传入 Worker 线程文件的路径来创建实例。例如,如果 worker 线程文件所在路径为 “entry/src/main/ets/workers/worker.ets”,则可以这样创建:

const workerStage1: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/worker.ets');

        在创建 Worker 线程时,需要手动管理 Worker 的生命周期,且最多同时运行的 Worker 子线程数量为 64 个。当不需要 Worker 时,应调用 terminate () 接口或 close () 方法主动销毁 Worker,避免资源浪费 。使用 Worker 模块时,需要在宿主线程中注册 onerror 接口,否则当 Worker 线程出现异常时会发生 jscrash 问题。不支持跨 HAP 使用 Worker 线程文件,引用 HAR/HSP 前,需要先配置对 HAR/HSP 的依赖 。

3.3 线程间通信机制

        在 HarmonyOS 中,线程间通信是确保各个线程协同工作的关键 :

线程间双向通信:当使用 TaskPool Worker 和 Worker 创建线程时,系统提供了相应的双向通信接口。以 Worker 线程为例,在宿主线程和 Worker 子线程中通过 onmessage 和 postMessage 方法进行消息传递,以实现数据交互和任务执行 。在主线程中创建 Worker 实例后,可以通过 postMessage 方法向 Worker 线程发送消息:

import worker from '@ohos.worker';

let wk = new worker.ThreadWorker("entry/ets/workers/worker.ts");

wk.postMessage("message from main thread.");

        而在 Worker 线程中,可以通过 onmessage 事件来接收主线程发送的消息,并进行相应的处理,处理完成后还可以通过 postMessage 方法向主线程返回结果:

import worker from '@ohos.worker';

let parent = worker.workerPort;

parent.onmessage = function(message) {

console.info("onmessage: " + message);

parent.postMessage("message from worker thread.");

};

线程内通信:在同一个线程内,HarmonyOS 提供了 EventHub 来进行通信。EventHub 提供了线程内发送和处理事件的能力,包括对事件订阅、取消订阅、触发事件等。以 UIAbility 组件与 UI 之间的数据同步为例,当 UI 组件发生某些状态变化时,可以通过 EventHub 触发一个事件,UIAbility 组件通过订阅该事件来获取最新的状态信息,从而实现数据的同步更新 。例如,在一个待办事项应用中,当用户在 UI 上勾选了一个待办事项时,UI 组件可以通过 EventHub 发送一个 “待办事项状态更新” 的事件,UIAbility 组件订阅该事件后,及时将该待办事项的状态更新到数据库中,保证数据的一致性 。

四、实际开发运用

4.1 场景示例

        在实际的 HarmonyOS 应用开发中,进程与线程的合理运用至关重要。以视频解压功能为例,当用户下载了一个较大的视频文件后,需要对其进行解压操作。如果直接在主线程中执行解压任务,由于解压过程通常较为耗时,会导致主线程被阻塞,应用界面无法响应用户的任何操作,如点击返回按钮、切换页面等,给用户带来极差的使用体验。因此,我们可以利用 Worker 线程来处理这个耗时的解压任务。

        在这个场景中,当用户点击解压按钮时,主线程创建一个 Worker 线程,并通过 postMessage 方法将解压任务的相关参数(如视频文件路径、解压目标路径等)发送给 Worker 线程。Worker 线程接收到消息后,开始执行解压操作。在解压过程中,Worker 线程可以实时将解压进度通过 postMessage 方法返回给主线程,主线程接收到进度消息后,更新界面上的进度条,让用户直观地了解解压的进展情况。这样,主线程始终保持响应状态,用户可以在解压过程中继续进行其他操作,极大地提升了应用的流畅性和用户体验 。

4.2 代码示例

        以下是一个简单的代码示例,展示了如何在 HarmonyOS 中创建进程(通过创建 ExtensionAbility)、线程,并实现它们之间的通信 :

创建 ExtensionAbility 进程(以 ServiceExtensionAbility 为例)

        首先,在 config.json 文件中注册 ServiceExtensionAbility:

{

"module": {

"abilities": [

{

"name": "com.example.myapp.MyServiceExtensionAbility",

"type": "service_extension",

"visible": true,

"exported": true,

"process": "com.example.myapp:service"

}

]

}

}

然后创建 MyServiceExtensionAbility 类:


import { ServiceExtensionAbility } from '@ohos.app.ability';

export default class MyServiceExtensionAbility extends ServiceExtensionAbility {

onCreate(want) {

super.onCreate(want);

console.info('MyServiceExtensionAbility onCreate');

// 在此处可以进行一些初始化操作,如启动后台任务等

}

onRequest(want, startId) {

console.info('MyServiceExtensionAbility onRequest');

// 处理请求逻辑

}

onDestroy() {

console.info('MyServiceExtensionAbility onDestroy');

// 清理资源

}

}

创建线程及通信(以 Worker 线程为例)

在主线程中创建 Worker 线程并发送消息:

import worker from '@ohos.worker';

import type common from '@ohos.app.ability.common';

// 创建Worker线程实例

let workerInstance: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/worker.ets');

// 获取UIAbilityContext

let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

// 向Worker线程发送消息

workerInstance.postMessage({ context, data: 'Hello from main thread' });

// 接收Worker线程返回的消息

workerInstance.onmessage = function (message) {

console.info('Received from worker: ', message.data);

};

在 Worker 线程文件(worker.ets)中接收和处理消息,并返回结果:

import worker from '@ohos.worker';

let parent = worker.workerPort;

parent.onmessage = function (message) {

console.info('Received from main thread: ', message.data);

// 处理接收到的消息

let result = 'Processed:'+ message.data;

// 向主线程返回消息

parent.postMessage(result);

};

        通过以上代码示例,我们可以看到在 HarmonyOS 开发中,如何创建进程和线程,并利用它们之间的通信机制实现复杂的业务逻辑。在实际开发中,开发者需要根据具体的业务需求和场景,灵活运用进程与线程,合理分配系统资源,以打造高效、稳定、流畅的应用程序 。

五、总结与展望

        HarmonyOS 的进程与线程管理机制为应用开发带来了高效、灵活且安全的解决方案。从进程类型的多样化,到线程创建与通信的丰富方式,HarmonyOS 为开发者提供了强大的工具集,使得开发者能够根据不同的业务场景和需求,优化应用的性能和用户体验。

        随着 HarmonyOS 生态的不断发展和壮大,未来其进程与线程管理机制有望在更多领域得到应用和拓展。在物联网领域,HarmonyOS 的分布式特性将使得不同设备之间的进程与线程协同更加紧密,实现设备间的无缝连接和资源共享 。在智能穿戴设备、智能家居等场景中,通过合理利用进程与线程,能够进一步提升设备的响应速度和运行效率,为用户带来更加智能化、便捷化的生活体验。

        对于广大开发者而言,深入理解和掌握 HarmonyOS 的进程与线程知识是迈向鸿蒙应用开发的重要一步。希望本文能为大家在 HarmonyOS 开发之路上提供有益的参考和帮助,鼓励大家积极探索,将这些知识运用到实际项目中,共同推动 HarmonyOS 生态的繁荣发展 。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容