本文为稀土技能社区首发签约文章,30天内制止转载,30天后未获授权制止转载,侵权必究!
系列介绍与导读
本系列是根据OpenHarmony中要害的ArkUI Engine 进行分析,所需求的源码可以经过以下链接下载arkui_ace_engine。UI是作为OpenHarmony体系中最要害的页面展示元素,UI结构的规划往往联系着体系流畅度的详细体现,本系列讲的engine,是指ArkUI的framework层,engine大部分代码由C++完结,其中少部分的ts/js归于部分映射
引荐阅读人员
本文根据分析的代码都是归于OpenHarmony, 与咱们听到的鸿蒙OS/鸿蒙next(星河版)仍是有些差异,一起本文注重于engine的完结,关于ArkUI的编写不涉及。引荐对体系感兴趣的开发者阅读。
学习完本系列后,你将会取得:
- engine是怎么把UI进行分层
- engine的三棵树
- engine是怎么对接渠道层
- 了解ArkUI 控件在C++的完结
环境配置
因而学习engine,咱们需求准备好可以编译C++的东西,便于咱们进行之后的流程盯梢,这里我引荐visual stuido,开发者可以经过C++扩展插件,进行C++环境的开始配置,如图
装置成功后,打开engine代码,那么你将会看到如下界面:
OpenHarmony 与鸿蒙OS联系
值得注意的是,本文所提到的鸿蒙OS,是指当时市面发行版别鸿蒙OS(4.0),并非鸿蒙next星河版别的组成
咱们以当时可以接触的鸿蒙OS(4.0)为比方,当时鸿蒙OS可以粗略看作由以下两部分组成
暂时无法在飞书文档外展示此内容
OpenHarmony:OpenHarmony是由一系列体系要害体系组成,比方内核、UI FrameWork等等,他可以被使用在各种嵌入式设备中,值得注意的是,在当时手机体系中, OpenHarmony更归于一个中心层,经过中心层去驱动渠道层的完结。比方ArkUI,其实是经过笼统层的,从而让ArkUI编写的代码可以在较低渠道依赖下进行
AOSP:Android开放源码,当时鸿蒙手机体系中的OpenHarmony完结,由AOSP完结,比方渠道相关烘托,渠道相关运转等
从技能与架构的视点动身,笔者以为华为的目标是,把OpenHarmony打造成AOSP的彻底笼统层,即便AOSP比OpenHarmony诞生要早得多。
AOSP implements OpenHarmony
可能大家会有误解,虽然AOSP 远远早于OpenHarmony,但是OpenHarmony其实是以AOSP先作为完结类再界说出接口,这样才能兼容当时所有的Andriod手机,因而后续即便剔除AOSP后,也可以持续以其他完结完结。但是以AOSP先作为完结类再规划出笼统的成果就是,接口界说与体系规划其实是受限于android的界说的。因而关于next版别的规划,信任广阔开发者都很猎奇。
UI Framework
UI Framework这个概念从事过相关移动端开发都不太生疏,咱们把Android原生体系,Flutter等UI Framework做一个分层,根据应用层到真实烘托层,主流的UI结构(体系完结)都满意下图:
当然,为了学习ArkUI Engine,咱们不需求都了解上面的几个分层,咱们再把上面的模型进行再一步的简化,得到下图
-
表明层:面向开发人员的最上层UI笼统,比方Android的View 或许Compose里边的Composable等,都是详细UI的笼统,比方Flutter的Widget
-
中心层:中心层,比方android 把View变成一个个RenderNode,用于进一步笼统,用于描绘Skia制作的命令等,其他UI结构比方Flutter也是,不同于Android的完结RenderNode,而是自己笼统一个个RenderObject,经过RenderObject生成Scene给到完结层(Flutter Engine)进行制作。
-
完结层:依托于详细图形标准完结,比方OpenGL/VulKan,这一层是真实的图形制作,比方咱们可以在Andorid直接经过OpenGL制作出形状。由于直接经过OpenGL制作会比较复杂,因而往往还会笼统出一层,比方Skia。
接下来,咱们将代入arkui engine的视角,去探索这三层怎么完结
表明层
咱们都知道Android的View体系中,每一个控件都可所以一个View,它其实就是Android中UI的表明,那么ArkUI中的表明是什么呢?
其实有两个,它们都在state_mgnt 目录下,分别是ViewPU 与 View
abstract class ViewPU extends NativeViewPartialUpdate
implements IViewPropertiesChangeSubscriber {
abstract class View extends NativeViewFullUpdate implements
IMultiPropertiesChangeSubscriber, IMultiPropertiesReadSubscriber {
它们的差异是两者的集成基类不同,一类是NativeViewPartialUpdate,用于增量更新,一类是NativeViewFullUpdate即全更新
大部分的控件都是承继于ViewPU,小部分特殊控件比方播放器可以承继于View
咱们在ArkTS写的各种Component,其实经过ArkCompiler编译后,就会得到编译后的js代码,比方咱们界说一个Component名称为Index,其实编译后就会变成一个js类,Index且集成于上面咱们提到的ViewPU,用于完结UI的表明
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
.....
编译后
ViewPU承载着UI的表明,比方保护parent的联系,local storage的保护等,最重要的是它将代表UI在JS的体现,详细的完结在C++里边。
咱们也讲到ViewPU承继于NativeViewPartialUpdate,NativeViewPartialUpdate界说如下,它里边的功能将经过napi完结js与C++的交互,即表明层与中心层的纽带
declare class NativeViewPartialUpdate {
constructor( );
markNeedUpdate(): void;
findChildById(compilerAssignedUniqueChildId: string): View;
syncInstanceId(): void;
isFirstRender(): boolean;
restoreInstanceId(): void;
static create(newView: NativeViewPartialUpdate): void;
finishUpdateFunc(elmtId: number): void;
isLazyItemRender(elmtId : number) : boolean;
setCardId(cardId: number): void;
getCardId(): number;
resetRecycleCustomNode(): void;
}
IViewPropertiesChangeSubscriber是一个接口,用于特点更新回调,这也是为什么咱们经过一些装修器可以完结特点值监听,其实实质仍是把特点作为监听者,当特点产生改变时其dependent也会进行相应从头制作
interface IViewPropertiesChangeSubscriber extends IPropertySubscriber {
// ViewPU get informed when View variable has changed
// informs the elmtIds that need update upon variable change
viewPropertyHasChanged(varName: PropertyInfo, dependentElmtIds: Set<number>): void ;
}
中心层
像Flutter,widget其实是相关于Element与RenderObject的笼统。相同的ArkUI也有类似概念,由于当时ArkUI默认情况下,使用了Flutter的管线烘托引擎进行烘托,即Layer之后到Scene这一层烘托,其实都是Flutter引擎制作。(想要了解Flutter引擎作业流程,这里引荐《Flutter内核源码剖析》这本书)
namespace OHOS::Ace::Flutter {
void Layer::AddChildren(const RefPtr<Layer>& layer)
{
auto it = std::find(children_.begin(), children_.end(), layer);
if (it == children_.end()) {
layer->SetParent(AceType::Claim(this));
children_.push_back(layer);
}
}
void Layer::DumpTree(int32_t depth)
{
if (DumpLog::GetInstance().GetDumpFile()) {
Dump();
DumpLog::GetInstance().Print(depth, AceType::TypeName(this), children_.size());
}
for (const auto& item : children_) {
item->DumpTree(depth + 1);
}
}
} // namespace OHOS::Ace::Flutter
之后Layer生成Scene进入Flutter Engine的光栅化。
已然最后走的是Flutter Engine的烘托,那么ArkUI也是用Element去表明中心成果吗?其实不是。由于Flutter的Element其实存在着大部分Widget相关的逻辑,直接用肯定是不可的,由于表明层都不相同,这也意味着中心层想要彻底迁移至Flutter Framework肯定是不可的。
对标于Element,ArkUI在这根底上提出了Componet这一概念,用于进一步笼统。Component作为一个最根底的容器,它的最重要一个效果是为了生成对应的Element,即CreateElement
class ACE_EXPORT Component : public virtual AceType {
DECLARE_ACE_TYPE(Component, AceType);
public:
Component();
~Component() override;
virtual RefPtr<Element> CreateElement() = 0;
咱们知道,一般的View可所以ViewGroup或许View,ViewGroup它的意图是把一些列可以烘托的View进行组合。关于ArkUI也是相同,关于一个可以承担烘托详细UI的View,Component在这根底上有一个RenderComponent的完结类,它除了生成Element之外,还要负责生成一个RenderNode,用于真实UI烘托。
class RenderComponent : public Component {
DECLARE_ACE_TYPE(RenderComponent, Component);
RenderComponent特有方法
virtual RefPtr<RenderNode> CreateRenderNode() = 0;
而负责组合其他Component的,比方ForEach这些,它的父类是BaseComposedComponent
// A component can compose others components.
class ACE_EXPORT BaseComposedComponent : public Component {
DECLARE_ACE_TYPE(BaseComposedComponent, Component);
咱们拿FlexComponent举比方,全部可烘托UI的,都将完结来自Component的CreateElement与RenderComponent的CreateRenderNode方法
ArkUI的Element与Flutter framwork的Element功能基本一致,负责子节点保护与构建等
class ACE_EXPORT Element : public virtual AceType {
DECLARE_ACE_TYPE(Element, AceType);
public:
Element() = default;
~Element();
void AddChild(const RefPtr<Element>& child, int32_t slot = DEFAULT_ELEMENT_SLOT);
void RemoveChild(const RefPtr<Element>& child);
RefPtr<Element> GetChildBySlot(int32_t slot);
void DeactivateChild(RefPtr<Element> child);
void Rebuild();
相同RenderNode负责视图烘托以及操作TreeRender,用于输出最后的Scene给pipeline进行完结层烘托。
// RenderNode is the base class for different render backend, represent a render unit for render pipeline.
class ACE_EXPORT RenderNode : public PropertyAnimatable, public AnimatableProperties, public virtual AceType {
DECLARE_ACE_TYPE(RenderNode, PropertyAnimatable, AceType);
public:
using OpacityCallback = std::function<void(uint8_t)>;
using SlipFactorSetting = std::function<void(double)>;
~RenderNode() override = default;
static void MarkTreeRender(const RefPtr<RenderNode>& root, bool& meetHole, bool needFlush);
static void MarkWholeRender(const WeakPtr<RenderNode>& nodeWeak, bool needFlush);
void SetZIndex(int32_t zIndex)
{
zIndex_ = zIndex;
}
int32_t GetZIndex() const
{
return zIndex_;
}
完结层
arkui的默认完结层与flutter完结层相同,用于终究渠道相关的输出,比方skia制作指令等,这里就不再胪陈,经过Layer合成Scene的过程见上述材料
void FlutterRenderContext::StartRecording()
{
currentLayer_ = AceType::MakeRefPtr<PictureLayer>();
recorder_ = flutter::PictureRecorder::Create();
canvas_ = flutter::Canvas::Create(
recorder_.get(), estimatedRect_.Left(), estimatedRect_.Top(), estimatedRect_.Right(), estimatedRect_.Bottom());
if (clipHole_.IsValid()) {
canvas_->save();
needRestoreHole_ = true;
canvas_->clipRect(
clipHole_.Left(), clipHole_.Top(), clipHole_.Right(), clipHole_.Bottom(), SkClipOp::kDifference);
}
containerLayer_->AddChildren(currentLayer_);
}
总结
本篇作为ArkUI Engine系列的导读,希望可以让读者对整体结构有一个大约的了解,经过建立较为全面的思想导图之后,接下来的学习就可以更加轻松。本系列将着重于中心层的完结,读者们需求对表明层的根底UI,比方ForEach,一般的View比方Row有个大致了解即可。
接下来,在接下来几篇文章中,咱们将更深化详细源码的完结,让大家更加了解engine的完结,bye~