OpenHarmony应用开发-线程间通讯与任务管理

素年锦时静待君丶
发布于 2023-4-10 17:30
浏览
0收藏

版本:v3.2 Beta5

线程模型

OpenHarmony应用中每个进程都会有一个主线程,主线程有如下职责:

  1. 负责管理其他线程
  2. 同应用多个UIAbility组件共用一个主线程
  3. 输入事件分发
  4. UI绘制
  5. 应用代码回调(事件处理,生命周期)
  6. 接收Worker发送的消息

除主线程外,还有一类与主线程并行的独立线程Worker,主要用于执行耗时操作,但不可以直接操作UI。Worker线程在主线程中创建,与主线程相互独立。最多可以创建7个Worker:

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区

基于OpenHarmony的线程模型,不同的业务功能运行在不同的线程上,业务功能的交互就需要线程间通信。线程间通信目前主要有Emitter和Worker两种方式,其中Emitter主要适用于线程间的事件同步, Worker主要用于新开一个线程执行耗时任务。

说明:

  • Stage模型只提供了主线程和Worker线程,Emitter主要用于主线程内或者主线程和Worker线程的事件同步。

使用Emitter进行线程间通信

​Emitter​​主要提供线程间发送和处理事件的能力,包括对持续订阅事件或单次订阅事件的处理、取消订阅事件、发送事件到事件队列等。

Emitter的开发步骤如下:

  1. 订阅事件

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);
  1. 发送事件

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的开发步骤如下:

  1. 在工程的​​模块级build-profile.json5​​文件的buildOption属性中添加配置信息。

  "buildOption": {
    "sourceOption": {
      "workers": [
        "./src/main/ets/workers/worker.ts"
      ]
    }
  }
  1. 根据build-profile.json5中的配置创建对应的worker.ts文件。

import worker from '@ohos.worker';

let parent = worker.workerPort;

// 处理来自主线程的消息
parent.onmessage = function(message) {
    console.info("onmessage: " + message)
    // 发送消息到主线程
    parent.postMessage("message from worker thread.")
}
  1. 主线程中使用如下方式初始化和使用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.ts");

// 发送消息到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.ts​​。
  • 主线程与Worker线程间支持的数据类型参考​​序列化支持类型​​。

任务管理场景介绍

任务管理相关的基本概念如下:

  • AbilityRecord:系统服务侧管理一个UIAbility实例的最小单元,对应一个应用侧的UIAbility组件实例。
  • MissionRecord:任务管理的最小单元。一个MissionRecord中仅有一个AbilityRecord,即一个UIAbility组件实例对应一个单独的任务。
  • MissionList:一个从桌面开始启动的任务列表,记录了任务之间的启动关系,上一个任务由下一个任务启动,最底部的任务由桌面启动,这里称之为任务链。
  • MissionListManager:系统任务管理模块,内部维护了当前所有的任务链,与最近任务列表保持一致。图1任务管理示意图

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区

任务的管理由系统应用(如桌面应用)负责,三方应用无法管理任务。用户通过最近任务列表进行任务的相关交互。当创建任务后,用户可以对最近任务列表进行如下操作:

  • 删除一个指定的任务。
  • 加锁或解锁一个指定的任务(加锁后的任务在清理所有任务时不会被清理)。
  • 清理最近任务列表中的所有任务。
  • 将一个指定的任务切换到前台。

一个UIAbility实例对应一个单独的任务,因此应用调用startAbility()方法启动一个UIAbility时,就是创建了一个任务。

桌面应用调用​​missionManager​​​的接口管理任务,需要申请​​ohos.permission.MANAGE_MISSIONS​​​权限,配置方式请参阅​​访问控制授权申请指导​​。

利用missionManager进行任务管理(监听任务变化、获取任务信息、获取任务快照、清理任务、任务加锁/解锁等),示例代码如下:

import missionManager from '@ohos.app.ability.missionManager'

let listener = {
    // 任务创建
    onMissionCreated: function (mission) {
        console.info("--------onMissionCreated-------")
    },
    // 任务销毁
    onMissionDestroyed: function (mission) {
        console.info("--------onMissionDestroyed-------")
    },
    // 任务快照变化
    onMissionSnapshotChanged: function (mission) {
        console.info("--------onMissionSnapshotChanged-------")
    },
    // 任务被移动到前台
    onMissionMovedToFront: function (mission) {
        console.info("--------onMissionMovedToFront-------")
    },
    // 任务图标变化
    onMissionIconUpdated: function (mission, icon) {
        console.info("--------onMissionIconUpdated-------")
    },
    // 任务名称变化
    onMissionLabelUpdated: function (mission) {
        console.info("--------onMissionLabelUpdated-------")
    },
    // 任务实例被关闭
    onMissionClosed: function (mission) {
        console.info("--------onMissionClosed-------")
    }
};

// 1.注册任务变化通知
let listenerId = missionManager.on('mission', listener);

// 2.获取系统最近20个任务
missionManager.getMissionInfos("", 20, (error, missions) => {
    console.info("getMissionInfos is called, error.code = " + error.code);
    console.info("size = " + missions.length);
    console.info("missions = " + JSON.stringify(missions));
});

// 3.获取单个任务的详细信息()
let missionId = 11; // 11只是示例,实际是从系统中获取的任务id,下面类似
let mission = missionManager.getMissionInfo("", missionId).catch(function (err) {
    console.info(err);
});

// 4.获取任务快照
missionManager.getMissionSnapShot("", missionId, (error, snapshot) => {
    console.info("getMissionSnapShot is called, error.code = " + error.code);
    console.info("bundleName = " + snapshot.ability.bundleName);
})

// 5.获取低分辨任务快照
missionManager.getLowResolutionMissionSnapShot("", missionId, (error, snapshot) => {
    console.info("getLowResolutionMissionSnapShot is called, error.code = " + error.code);
    console.info("bundleName = " + snapshot.ability.bundleName);
})

// 6.加锁/解锁任务
missionManager.lockMission(missionId).then(() => {
    console.info("lockMission is called ");
});

missionManager.unlockMission(missionId).then(() => {
    console.info("unlockMission is called ");
});

// 7.把任务切到前台
missionManager.moveMissionToFront(missionId).then(() => {
    console.info("moveMissionToFront is called ");
});

// 8.删除单个任务
missionManager.clearMission(missionId).then(() => {
    console.info("clearMission is called ");
});

// 9.删除全部任务
missionManager.clearAllMissions().catch(function (err) {
    console.info(err);
});

// 10.解注册任务变化通知
missionManager.off('mission', listenerId, (error) => {
    console.info("unregisterMissionListener");
})

任务管理与启动模式

如前文所述,一个UIAbility实例对应一个任务。UIAbility实例个数与UIAbility配置的启动模式有关。在FA模型下,通过config.json配置文件中的“launchType”属性配置;在Stage模型下,通过​​module.json5配置文件​​中的“launchType”属性配置。

下面介绍了任务管理如何实现以下三种启动模式UIAbility组件的管理:

  • singleton:单实例模式,应用在运行时只存在一个该UIAbility实例。

图1 任务与singleton模式

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区

  • standard:多实例模式,每次调用startAbility()方法,都会在应用进程中创建一个该Ability的实例。

图2 任务与standard模式

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区

图3 任务与specified模式

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区

每个UIAbility实例都对应了一个最近任务列表中看到的Mission(任务)。

每个UIAbility实例对应的Mission都保留有该UIAbility实例的快照(Snapshot),UIAbility实例销毁后,Mission信息(包括Ability信息和任务快照)依然会保留,直到用户删除该任务。

说明: specified模式只在Stage模型上支持,FA模型不支持。

页面栈及任务链

页面栈

单个UIAbility组件可以实现多个页面,并在多个页面之间跳转,这种UIAbility组件内部的页面跳转关系称为“页面栈”,由ArkUI框架统一管理,如下图中的UIAbility1的Page1->Page2->Page3和UIAbility2的PageA->PageB->PageC。

图1 页面栈示意图  

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区

  • 页面栈的形成(下面2/3/5/6步骤为页面跳转,由ArkUI管理)
  1. 点击桌面图标(​​startAbility​​)启动UIAbility1,UIAbility1的初始页面为Page1。
  2. 点击Page1页面按钮(​​Navigator​​)跳转到Page2页面。
  3. 点击Page2页面按钮(​​Navigator​​)跳转到Page3页面。
  4. 点击Page3页面按钮(​​startAbility​​)跳转到UIAbility2,UIAbility2的初始页面为PageA。
  5. 点击PageA页面按钮(​​Navigator​​)跳转到PageB页面。
  6. 点击PageB页面按钮(​​Navigator​​)跳转到PageC页面。


  • 页面栈的返回(下面1/2/4/5步骤为页面跳转,由ArkUI管理)
  1. 在UIAbility2的PageC页面点击返回键回到UIAbility2的PageB页面。
  2. 在UIAbility2的PageB页面点击返回键回到UIAbility2的PageA页面。
  3. 在UIAbility2的PageA页面点击返回键跳转到UIAbility1的Page3页面。
  4. 在UIAbility1的Page3页面点击返回键回到UIAbility1的Page2页面。
  5. 在UIAbility1的Page2页面点击返回键回到UIAbility1的Page1页面。
  6. 在UIAbility1的Page1页面点击返回键回到桌面。

任务链

上文介绍了页面栈的返回,如果Ability2页面栈一层层通过返回键返回到最底层,再次点击返回键时,会返回到Ability1。因为在MissionList中记录了任务(Mission)之间的启动关系,即如果Ability1通过startAbility启动Ability2,则会形成一个MissionList任务链:Ability1->Ability2,当Ability2页面栈返回到首页时,再次点击返回键,会返回到Ability1的页面。

MissionList任务链记录了任务之间的拉起关系,但是这个任务链可能会断开,有以下几种情况会导致任务链的断开:

  • 进入任务列表,把任务链中间某个任务移动到前台。

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区

  • 进入任务列表,把任务链中间某个任务清理掉。

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区

  • 单实例UIAbility的任务,被不同的任务反复拉起(AbilityB为单例)。

OpenHarmony应用开发-线程间通讯与任务管理-鸿蒙开发者社区




文章转载自:​​https://docs.openharmony.cn/pages/v3.2Beta/zh-cn/application-dev/application-models/page-mission-stack.md/​

已于2023-4-10 17:30:24修改
收藏
回复
举报
回复
    相关推荐