前言
HarmonyOS在演进过程中,先后提出了两种应用模型:FA(Feature Ability)模型和Stage模型。其中,FA模型是早期推出的,支持两种开发范式:类Web范式和声明式范式;而Stage模型作为当前主推的应用模型,仅支持声明式范式。类Web范式与声明式范式最直观的区别在于:前者基于JavaScript语言,所构建的应用中每个页面对应三个文件(.js、.hml和.css);后者基于ArkTS语言(扩展的TypeScript语言),所构建的应用中每个页面对应一个文件(.ets)。
本文不过多关注HarmonyOS中应用模型和开发范式的具体内容,而是聚焦于支撑应用构建用户界面的ArkUI开发框架中的线程模型。后文将对ArkUI线程模型的代码实现以及在不同应用模型和开发范式上的区别进行详细介绍。
1. 线程模型组成
本小节将对ArkUI线程模型的组成结构进行介绍,同时指出不同开发范式以及应用模型在线程模型上的不同之处。
ArkUI框架将任务类型分为如下六种:Platform、JS、UI、GPU、IO和Background。其中,Background类型任务由一个线程池(默认包含八个线程)负责处理,其余类型任务均由单个线程承载。具体的,各类任务对应的线程功能如下:
需要说明的是,ArkUI中不同应用模型或不同开发范式对应的线程模型是有差异的。区别如下:
●类Web范式:Platform、JS和UI类型的任务会由三个不同的线程执行。
●声明式范式FA模型:Platform类型任务由一个线程执行,JS和UI类型由同一个线程执行。
●声明式范式Stage模型:Platform、JS和UI类型的任务共用同一个线程执行。
另外,目前ArkUI中,IO类型任务会与UI类型任务共用同一个线程执行。且在使能Rosen后端后,GPU类型任务会与UI类型任务也会共用同一个线程执行。后续介绍代码时,会再做说明。
2. 线程模型实现
本小节将对ArkUI线程模型相关代码逐个说明。ArkUI代码仓中,与线程模型相关的主要是下图中的几个文件:
.
├── adapter
│ ├── ohos
│ │ └── entrance
│ │ ├── ace_container.h
│ │ └── ace_container.cpp
│ └── preview
│ └── entrance
│ ├── ace_container.h
│ └── ace_container.cpp
└── frameworks
├── base
│ └── thread
│ ├── background_task_executor.h
| ├── background_task_executor.cpp
│ ├── cancelable_callback.h
| ├── task_executor.h
| └── frame_trace_adapter.h
└── core
└── common
└── flutter
├── flutter_task_executor.h
├── flutter_task_executor.cpp
├── flutter_thread_model.h
└── flutter_thread_model.cpp
在ArkUI框架中,各种类型的任务是通过任务执行器进行管理的,是整个线程模型的核心。
0.任务执行器定义于文件tast_executor.h和flutter_task_executor.h (.cpp) 中,前者定义了任务执行器的基类TaskExecutor,后者定义了任务执行器基于Flutter线程库的实现类FlutterTaskExecutor。
1.在FlutterTaskExecutor中,Background类型的任务会交给后台任务执行器去处理,由文件background_task_executor.h (.cpp) 中的BackgroundTaskExecutor类定义。其他类型的任务由基于flutter线程库创建的五个fml::TaskRunner实例进行处理。
2.文件cancelable_callback.h中,给出了一个基本任务的描述,定义为类CancelableCallback,由其内部类Callback辅助实现。
除此之外,ace_container.h (.cpp) 和flutter_thread_model.h (.cpp) 主要和任务执行器的创建相关。
2.1 任务执行器
任务执行器是整个线程模型的核心,负责将各种类型的任务交给对应的线程或者线程池进行执行。本小节分将对任务执行器的基类TaskExecutor和基于Flutter中线程库的实现类FlutterTaskExecutor进行介绍。
基类TaskExecutor
在TaskExecutor中,主要给出了ArkUI中任务类型的定义以及用于抛同步和异步任务的接口。
如前文所述,ArkUI中的任务类型分为六类,与之呼应,可以看到在tast_executor.h中定义了如下枚举类TaskType:
enum class TaskType : uint32_t {
PLATFORM = 0,
UI,
IO,
GPU,
JS,
BACKGROUND,
UNKNOWN,
};
在整个ArkUI仓中,将某个任务抛到对应线程执行时,都会以此枚举类型进行标定。
下表对TaskExecutor中提供的所有接口进行简单分类,以说明其作用:
可以看到,TaskExecutor类中主要是给出了抛同步和异步任务的接口,基本每个接口的形参都有左、右值引用两种形式。在平时开发中,为了提高一丝性能,可以优先选择调用形参为右值的接口,这类接口可以省去一些创建和拷贝实参的性能消耗。具体到代码,可以看到TaskExecutor类提供的接口实现并不复杂,需要特别说明的是:
●异步类型的接口:最终都会调用到虚