鸿蒙开发——进程模型与进程通信

209 阅读5分钟

1、进程模型

❓ 什么是进程?

进程是一个正在执行的程序的实例。当我们启动一个程序时,操作系统会创建一个进程,分配给它所需的资源,如内存和CPU时间。每个进程至少有一个线程,即执行线程,负责执行程序的指令。进程是操作系统进行资源分配和调度的基本单位,是操作系统提供给用户和应用程序的一个抽象概念。

在鸿蒙的进程模型中,进程被分为三类:主进程(负责UI的进程)、扩展进程(负责大部分ExtensionAbility)、WebView渲染进程(负责网页渲染的独立进程)。

  • 主进程

应用中(同一Bundle名称)的所有UIAbility、ServiceExtensionAbility和DataShareExtensionAbility均是运行在同一个独立进程(主进程)中,如下图中绿色部分的“Main Process”。

仅系统应用支持构建ServiceExtensionAbility和DataShareExtensionAbility
  • 扩展进程(可能有多个)

应用中(同一Bundle名称)的所有同一类型ExtensionAbility(除ServiceExtensionAbility、DataShareExtensionAbility外)均是运行在一个独立进程中,如下图中蓝色部分的“FormExtensionAbility Process”、“InputMethodExtensionAbility Process”、其他ExtensionAbility Process。

  • WebView进程

WebView拥有独立的渲染进程,如下图中黄色部分的“Render Process”。

image.png

2、进程间通信方式

在鸿蒙开发中,需要跨进程通信的原因,是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源。

进程间通信一般有两种方式:1)IPC/RPC;2)公共事件机制。

  • IPC/RPC

IPC(Inter-Process Communication): 使用Binder驱动,用于设备内的跨进程通信。

RPC(Remote Procedure Call):使用软总线驱动,用于跨设备跨进程通信。

由于篇幅原因,IPC和RPC在未来的文章中展开介绍
  • 公共事件机制

公共事件机制多用于一对多的通信场景(公共事件发布者可能存在多个订阅者同时接收事件)

基于鸿蒙的进程模型,针对应用间和应用内存在多个进程的情况,推荐使用公共事件机制来实现进程间通信。

3、公共事件机制

CES(Common Event Service,公共事件服务)为应用程序提供订阅、发布、退订公共事件的能力。

公共事件从系统角度可分为:系统公共事件和自定义公共事件。

  • 系统公共事件:CES内部定义的公共事件,当前仅支持系统应用和系统服务发布,例如HAP安装、更新、卸载等公共事件(系统公共事件参考官方文档定义:developer.huawei.com/consumer/cn…

  • 自定义公共事件:应用定义的公共事件,可用于实现跨进程的事件通信能力。

公共事件按发送方式可分为:无序公共事件、有序公共事件和粘性公共事件。

  • 无序公共事件:CES在转发公共事件时,不考虑订阅者是否接收到该事件,也不保证订阅者接收到该事件的顺序与其订阅顺序一致。

  • 有序公共事件:CES在转发公共事件时,根据订阅者设置的优先级等级,优先将公共事件发送给优先级较高的订阅者,等待其成功接收该公共事件之后再将事件发送给优先级较低的订阅者。如果有多个订阅者具有相同的优先级,则他们将随机接收到公共事件。

  • 粘性公共事件:能够让订阅者收到在订阅前已经发送的公共事件就是粘性公共事件。【普通的公共事件只能在订阅后发送才能收到,而粘性公共事件的特殊性就是可以先发送后订阅,同时也支持先订阅后发送。发送粘性事件必须是系统应用或系统服务,粘性事件发送后会一直存在系统中,且发送者需要申请ohos.permission.COMMONEVENT_STICKY权限】

每个应用都可以按需订阅公共事件,订阅成功,当公共事件发布时,系统会将其发送给对应的应用。这些公共事件可能来自系统、其他应用和应用自身。示意图如下:

image.png

3.1、发布公共事件

当需要发布某个自定义公共事件时,可以通过publish()方法发布事件。发布的公共事件可以携带数据,供订阅者解析并进行下一步处理。发布公共事件接口定义如下:

// 发布简单的公共事件
publish(event: string, callback: AsyncCallback)

// 指定发布信息并发布公共事件
publish(event: string, options: CommonEventPublishData, callback: AsyncCallback)

// CommonEventPublishData 结构定义如下:
class CommonEventPublishData {
// 表示订阅者包名称,只有包名为bundleName的订阅者才能收到该公共事件。
bundleName: string
// 表示公共事件的结果代码。
code: number
// 表示公共事件的自定义结果数据。
data: string
// 表示订阅者的权限。
subscriberPermissions: Array<string>
// 表示是否是有序事件。
isOrdered: boolean
// 表示是否是粘性事件。仅系统应用或系统服务允许发送粘性事件。
isSticky: boolean
// 表示公共事件的附加信息。
parameters: {[key: string]: any}
}

👉🏻 发布一个不携带信息的公共事件

不携带信息的公共事件,只能发布无序公共事件。

// 1. 导入模块
import { BusinessError, commonEventManager } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = 'ProcessModel';
const DOMAIN_NUMBER: number = 0xFF00;

// ...

// 2. 传入需要发布的事件名称和回调函数,发布事件。
// 发布公共事件,其中的event字段需要替换为实际的事件名称。
commonEventManager.publish('event', (err: BusinessError) => {
  if (err) {
    hilog.info(DOMAIN_NUMBER, TAG, `PublishCallBack err = ${JSON.stringify(err)}`);
  } else {
    //...
    hilog.info(DOMAIN_NUMBER, TAG, `Publish success`);
  }
});

👉🏻 发布一个携带信息的公共事件

携带信息的公共事件,可以发布为无序公共事件、有序公共事件和粘性事件,可以通过参数CommonEventPublishData的isOrdered、isSticky的字段进行设置。

// 导入模块
import { BusinessError, commonEventManager } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = 'ProcessModel';
const DOMAIN_NUMBER: number = 0xFF00;

// 2. 构建需要发布的公共事件信息。
let options: commonEventManager.CommonEventPublishData = {
  code: 1, // 公共事件的初始代码
  data: 'initial data', // 公共事件的初始数据
};

// 3.传入需要发布的事件名称、需要发布的指定信息和回调函数,发布事件。
// 发布公共事件,其中的event字段需要替换为实际的事件名称。
commonEventManager.publish('event', options, (err: BusinessError) => {
  if (err) {
    hilog.error(DOMAIN_NUMBER, TAG, 'PublishCallBack err = ' + JSON.stringify(err));
  } else {
    //...
    hilog.info(DOMAIN_NUMBER, TAG, 'Publish success');
  }
});

3.2、动态订阅公共事件

动态订阅是指当应用在运行状态时对某个公共事件进行订阅,在运行期间如果有订阅的事件发布那么订阅了这个事件的应用将会收到该事件及其传递的参数。(例如,某应用希望在其运行期间收到电量过低的事件,并根据该事件降低其运行功耗,那么该应用便可动态订阅电量过低事件,收到该事件后关闭一些非必要的任务来降低功耗)

动态订阅公共事件的接口定义如下:

// 创建订阅者对象(Callback和Promise版本)
reateSubscriber(subscribeInfo: CommonEventSubscribeInfo, callback: AsyncCallback<CommonEventSubscriber>): void
createSubscriber(subscribeInfo: CommonEventSubscribeInfo): Promise<CommonEventSubscriber>

// 订阅公共事件。
subscribe(subscriber: CommonEventSubscriber, callback: AsyncCallback): void

// CommonEventSubscribeInfo结构定义如下:
class CommonEventSubscribeInfo {
// 表示要订阅的公共事件。
events: Array<string>
// 表示发布者的权限,订阅方将只能接收到具有该权限的发送方发布的事件。
publisherPermission: string
// 表示设备ID,该值必须是同一ohos网络上的现有设备ID。通过@ohos.deviceInfo获取udid,作为订阅者的设备ID。
publisherDeviceId: string
// 表示用户ID。参数可选,默认值当前用户的ID。如果指定了此参数,则该值必须是系统中现有的用户ID。通过getOsAccountLocalId获取系统账号ID,作为订阅者的用户ID。
userId: number
// 表示订阅者的优先级。值的范围是-100到1000,超过上下限的优先级将被设置为上下限值。
priority: number
//表示要订阅的发布者的bundleName。
publisherBundleName: string
}

👉🏻 动态订阅示例代码如下:

// 1. 导入模块
import { BusinessError, commonEventManager } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = 'ProcessModel';
const DOMAIN_NUMBER: number = 0xFF00;

// 2. 创建订阅者信息(结构详见上文中的CommonEventSubscribeInfo)
// 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作
let subscriber: commonEventManager.CommonEventSubscriber | null = null;
// 订阅者信息,其中的event字段需要替换为实际的事件名称。
let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
    events: ['event'], // 订阅灭屏公共事件
};

// 3.创建订阅者,保存返回的订阅者对象subscriber,用于执行后续的订阅、退订等操作。
// 创建订阅者回调
commonEventManager.createSubscriber(subscribeInfo, (err: BusinessError, data: commonEventManager.CommonEventSubscriber) => {
  if (err) {
    hilog.error(DOMAIN_NUMBER, TAG, `Failed to create subscriber. Code is ${err.code}, message is ${err.message}`);
    return;
  }
  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in creating subscriber.');
  subscriber = data;
})

//4. 创建订阅回调函数,订阅回调函数会在接收到事件时触发。订阅回调函数返回的data内包含了公共事件的名称、发布者携带的数据等信息
// 订阅公共事件回调
if (subscriber !== null) {
  commonEventManager.subscribe(subscriber, (err: BusinessError, data: commonEventManager.CommonEventData) => {
    if (err) {
      hilog.error(DOMAIN_NUMBER, TAG, `Failed to subscribe common event. Code is ${err.code}, message is ${err.message}`);
      return;
    }
    // ...
  })
} else {
  hilog.error(DOMAIN_NUMBER, TAG, `Need create subscriber`);
}

⭐️ 其中,订阅回调函数中CommonEventData数据结构定义如下:

class CommonEventData {
// 表示当前接收的公共事件名称。
event: string
// 表示包名称,当前默认为空。
bundleNamestring
// 表示公共事件的结果代码,用于传递int类型的数据。
code: number
// 表示公共事件的自定义结果数据,用于传递string类型的数据。
data: string
// 表示公共事件的附加信息。
parameters: {[keystring]: any}
}

3.3、取消动态订阅公共事件

动态订阅者完成业务需要时,需要主动取消订阅,订阅者通过调用unsubscribe()方法取消订阅事件。接口定义如下:

// 取消订阅公共事件
unsubscribe(subscriber: CommonEventSubscriber, callback?: AsyncCallback)

👉🏻 取消动态订阅公共事件示例如下:

// 1. 导入模块
import { BusinessError, commonEventManager } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = 'ProcessModel';
const DOMAIN_NUMBER: number = 0xFF00;

// 2. 根据3.2步骤中介绍的,订阅某个公共事件

// 3. 调用CommonEvent中的unsubscribe()方法取消订阅某事件
// subscriber为订阅事件时创建的订阅者对象
if (this.subscriber !== null) {
  commonEventManager.unsubscribe(this.subscriber, (err: BusinessError) => {
    if (err) {
      hilog.error(DOMAIN_NUMBER, TAG, `UnsubscribeCallBack err = ${JSON.stringify(err)}`);
    } else {
      hilog.info(DOMAIN_NUMBER, TAG, `Unsubscribe success`);
      this.subscriber = null;
    }
  })
}