HarmonyOS Developer Stage 模型开发指导
进程间通讯
进程模型
HarmonyOS的进程模型:
- 应用中(同一包名)的所有UIAbility、ServiceExtensionAbility运行在同一个独立进程中。
- WebView拥有独立的渲染进程。
基于HarmonyOS的进程模型,系统中应用间和应用内都会存在多个进程的情况,因此系统提供了如下两种进程间通信机制:
- 公共事件机制:多用于一对多的通信场景,公共事件发布者可能存在多个订阅者同时接收事件。
- 后台服务机制:当前主要通过ServiceExtensionAbility的能力实现。
公共事件
公共事件简介
HarmonyOS通过CES(Common Event Service,公共事件服务)为应用程序提供订阅、发布、退订公共事件的能力。
公共事件从系统角度可分为:系统公共事件和自定义公共事件。
- 系统公共事件:CES内部定义的公共事件,只有系统应用和系统服务才能发布,例如HAP安装,更新,卸载等公共事件。目前支持的系统公共事件详见系统公共事件列表。
- 自定义公共事件:应用自定义一些公共事件用来实现跨进程的事件通信能力。
公共事件按发送方式可分为:无序公共事件、有序公共事件和粘性公共事件。
- 无序公共事件:CES转发公共事件时,不考虑订阅者是否接收到,按订阅者订阅先后顺序转发。
- 有序公共事件:CES转发公共事件时,按订阅者订阅先后顺序,在接收到前一个订阅者回复后,再转发下一个订阅者。
- 粘性公共事件:能够让订阅者收到在订阅前已经发送的公共事件就是粘性公共事件。普通的公共事件只能在订阅后发送才能收到,而粘性公共事件的特殊性就是可以先发送后订阅。发送粘性事件必须是系统应用或系统服务,且需要申请ohos.permission.COMMONEVENT_STICKY权限,配置方式请参阅访问控制授权申请指导。
每个应用都可以按需订阅公共事件,订阅成功,当公共事件发布时,系统会将其发送给对应的应用。这些公共事件可能来自系统、其他应用和应用自身。
图1 公共事件示意图
公共事件订阅
场景介绍
当需要订阅某个公共事件,获取该公共事件传递的参数时,需要创建一个订阅者对象,用于作为订阅公共事件的载体,订阅公共事件并获取公共事件传递而来的参数。订阅部分系统公共事件需要先申请权限,订阅这些事件所需要的权限请见公共事件权限列表。
接口说明
详细接口见接口文档。
接口名 | 接口描述 |
createSubscriber(subscribeInfo: CommonEventSubscribeInfo, callback: AsyncCallback<CommonEventData>): void | 创建订阅者对象(callback) |
createSubscriber(subscribeInfo: CommonEventSubscribeInfo): Promise<CommonEventSubscriber> | 创建订阅者对象(promise) |
subscribe(subscriber: CommonEventSubscriber, callback: AsyncCallback): void | 订阅公共事件 |
开发步骤
- 导入CommonEvent模块。
import commonEvent from '@ohos.commonEventManager';
- 创建订阅者信息,详细的订阅者信息数据类型及包含的参数请见CommonEventSubscribeInfo文档介绍。
// 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作
let subscriber = null;
// 订阅者信息
let subscribeInfo = {
events: ["usual.event.SCREEN_OFF"], // 订阅灭屏公共事件
}
- 创建订阅者,保存返回的订阅者对象subscriber,用于执行后续的订阅、退订等操作。
// 创建订阅者回调
commonEvent.createSubscriber(subscribeInfo, (err, data) => {
if (err) {
console.error(`[CommonEvent] CreateSubscriberCallBack err=${JSON.stringify(err)}`);
} else {
console.info(`[CommonEvent] CreateSubscriber success`);
subscriber = data;
// 订阅公共事件回调
}
})
- 创建订阅回调函数,订阅回调函数会在接收到事件时触发。订阅回调函数返回的data内包含了公共事件的名称、发布者携带的数据等信息,公共事件数据的详细参数和数据类型请见CommonEventData文档介绍。
// 订阅公共事件回调
if (subscriber !== null) {
commonEvent.subscribe(subscriber, (err, data) => {
if (err) {
console.error(`[CommonEvent] SubscribeCallBack err=${JSON.stringify(err)}`);
} else {
console.info(`[CommonEvent] SubscribeCallBack data=${JSON.stringify(data)}`);
}
})
} else {
console.error(`[CommonEvent] Need create subscriber`);
}
公共事件发布
场景介绍
当需要发布某个自定义公共事件时,可以通过publish()方法发布事件。发布的公共事件可以携带数据,供订阅者解析并进行下一步处理。
注意
已发出的粘性公共事件后来订阅者也可以接收到,其他公共事件都需要先订阅再接收,订阅参考公共事件订阅章节。
接口说明
详细接口见接口文档。
接口名 | 接口描述 |
publish(event: string, callback: AsyncCallback) | 发布公共事件。 |
publish(event: string, options: CommonEventPublishData, callback: AsyncCallback) | 指定发布信息并发布公共事件。 |
发布不携带信息的公共事件
不携带信息的公共事件,只能发布无序公共事件。
- 导入CommonEvent模块。
import commonEvent from '@ohos.commonEventManager';
- 传入需要发布的事件名称和回调函数,发布事件。
// 发布公共事件
commonEvent.publish("usual.event.SCREEN_OFF", (err) => {
if (err) {
console.error(`[CommonEvent] PublishCallBack err=${JSON.stringify(err)}`);
} else {
console.info(`[CommonEvent] Publish success`);
}
})
发布携带信息的公共事件
携带信息的公共事件,可以发布为无序公共事件、有序公共事件和粘性事件,可以通过参数CommonEventPublishData的isOrdered、isSticky的字段进行设置。
- 导入CommonEvent模块。
import commonEvent from '@ohos.commonEventManager';
- 传入需要发布的事件名称和回调函数,发布事件。
// 公共事件相关信息
let options = {
code: 1, // 公共事件的初始代码
data: "initial data", // 公共事件的初始数据
}
- 传入需要发布的事件名称、需要发布的指定信息和回调函数,发布事件。
// 发布公共事件
commonEvent.publish("usual.event.SCREEN_OFF", options, (err) => {
if (err) {
console.error('[CommonEvent] PublishCallBack err=' + JSON.stringify(err));
} else {
console.info('[CommonEvent] Publish success')
}
})
公共事件取消订阅
场景介绍
订阅者完成业务需要时,需要主动取消订阅,订阅者通过下面方法取消订阅事件。
接口说明
接口名 | 接口描述 |
unsubscribe(subscriber: CommonEventSubscriber, callback?: AsyncCallback) | 取消订阅公共事件 |
开发步骤
- 导入CommonEvent模块。
import commonEvent from '@ohos.commonEventManager';
- 根据事件订阅章节的步骤来订阅某个事件。
- 调用CommonEvent中的unsubscribe方法取消订阅某事件。
// subscriber为订阅事件时创建的订阅者对象
if (subscriber !== null) {
commonEvent.unsubscribe(subscriber, (err) => {
if (err) {
console.error(`[CommonEvent] UnsubscribeCallBack err=${JSON.stringify(err)}`)
} else {
console.info(`[CommonEvent] Unsubscribe`)
subscriber = null
}
})
}
后台服务
Stage模型提供了ServiceExtensionAbility来提供后台服务的能力,支持系统应用实现一个后台服务并对外提供相应的能力;系统应用A实现了一个后台服务,三方应用B可以通过连接系统应用A的后台服务与其进行进程间通信。
ServiceExtensionAbility的详细介绍请参见后台服务开发指导。
线程间通讯
线程模型
HarmonyOS应用中每个进程都会有一个主线程,主线程有如下职责:
- 负责管理其他线程
- 同应用多个UIAbility组件共用一个主线程
- 输入事件分发
- UI绘制
- 应用代码回调(事件处理,生命周期)
- 接收Worker发送的消息
除主线程外,还有一类与主线程并行的独立线程Worker,主要用于执行耗时操作,但不可以直接操作UI。Worker线程在主线程中创建,与主线程相互独立。最多可以创建7个Worker:
基于HarmonyOS的线程模型,不同的业务功能运行在不同的线程上,业务功能的交互就需要线程间通信。线程间通信目前主要有Emitter和Worker两种方式,其中Emitter主要适用于线程间的事件同步, Worker主要用于新开一个线程执行耗时任务。
说明:
- Stage模型只提供了主线程和Worker线程,Emitter主要用于主线程内或者主线程和Worker线程的事件同步。
使用Emitter进行线程间通信
Emitter主要提供线程间发送和处理事件的能力,包括对持续订阅事件或单次订阅事件的处理、取消订阅事件、发送事件到事件队列等。
Emitter的开发步骤如下:
- 订阅事件
import emitter from "@ohos.events.emitter";
// 定义一个eventId为1的事件
let event = {
eventId: 1
};
// 收到eventId为1的事件后执行该回调
let callback = (eventData) => {
console.info('event callback');
};
// 订阅eventId为1的事件
emitter.on(event, callback);
- 发送事件
import emitter from "@ohos.events.emitter";
// 定义一个eventId为1的事件,事件优先级为Low
let event = {
eventId: 1,
priority: emitter.EventPriority.LOW
};
let eventData = {
data: {
"content": "c",
"id": 1,
"isEmpty": false,
}
};
// 发送eventId为1的事件,事件内容为eventData
emitter.emit(event, eventData);
使用Worker进行线程间通信
Worker是与主线程并行的独立线程。创建Worker的线程被称为宿主线程,Worker工作的线程被称为Worker线程。创建Worker时传入的脚本文件在Worker线程中执行,通常在Worker线程中处理耗时的操作,需要注意的是,Worker中不能直接更新Page。
Worker的开发步骤如下:
- 在工程的模块级build-profile.json5文件的buildOption属性中添加配置信息。
"buildOption": {
"sourceOption": {
"workers": [
"./src/main/ets/workers/worker.ts"
]
}
}
- 根据build-profile.json5中的配置创建对应的worker.js文件。
import worker from '@ohos.worker';
let parent = worker.workerPort;
// 处理来自主线程的消息
parent.onmessage = function(message) {
console.info("onmessage: " + message)
// 发送消息到主线程
parent.postMessage("message from worker thread.")
}
- 主线程中使用如下方式初始化和使用worker。
- Stage模型:
import worker from '@ohos.worker';
let wk = new worker.ThreadWorker("entry/ets/workers/worker.ts");
// 发送消息到worker线程
wk.postMessage("message from main thread.")
// 处理来自worker线程的消息
wk.onmessage = function(message) {
console.info("message from worker: " + message)
// 根据业务按需停止worker线程
wk.terminate()
}
- FA模型:
import worker from '@ohos.worker';
let wk = new worker.ThreadWorker("../workers/worker.js");
// 发送消息到worker线程
wk.postMessage("message from main thread.")
// 处理来自worker线程的消息
wk.onmessage = function(message) {
console.info("message from worker: " + message)
// 根据业务按需停止worker线程
wk.terminate()
}
说明:
- build-profile.json5中配置的worker.ts的相对路径都为./src/main/ets/workers/worker.ts时,在Stage模型下创建worker需要传入路径entry/ets/workers/worker.ts;在FA模型下创建worker需要传入路径../workers/worker.js。
- 主线程与Worker线程间支持的数据类型参考序列化支持类型。
Stage模型应用配置文件
应用配置文件中包含应用配置信息、应用组件信息、权限信息、开发者自定义信息等,这些信息在编译构建、分发和运行阶段分别提供给编译工具、应用市场和操作系统使用。
在基于Stage模型开发的应用项目代码下,都存在app.json5(一个)及module.json5(一个或多个)两种配置文件,常用配置项请参见应用/组件级配置。对于这两种配置文件的更多介绍,请参见应用配置文件(Stage模型)。