页面首次创建流程介绍
流程示意
- 框架初始化:主要初始化框架的上下文环境,包括containter容器、pipeline渲染管线、rootNode根节点等;
- 页面加载:在框架初始化完成后,会执行页面文件,页面文件执行时会生成后端的页面节点树,并挂载到渲染管线的根节点上;
- 布局渲染:页面节点挂载完成后,会触发相应的布局和渲染任务,进行布局渲染。
下面分别从这三个阶段具体展开进行分析说明。
框架初始化
Ability初始化
Ability是系统调度应用的最小单元,通过JS引擎隔离开发者代码。其中,每个Ability都有自己的UI线程,实现并行渲染。
- 初始化当前进程
std::call_once(onceFlag, [abilityContext, cacheDir]() {
LOGI("Initialize for current process.");
SetHwIcuDirectory();
Container::UpdateCurrent(INSTANCE_ID_PLATFORM);
CapabilityRegistry::Register();
AceApplicationInfo::GetInstance().SetPackageName(abilityContext->GetBundleName());
AceApplicationInfo::GetInstance().SetDataFileDirPath(abilityContext->GetFilesDir());
AceApplicationInfo::GetInstance().SetApiTargetVersion(abilityContext->GetApplicationInfo()->apiTargetVersion);
AceApplicationInfo::GetInstance().SetAppVersionName(abilityContext->GetApplicationInfo()->versionName);
AceApplicationInfo::GetInstance().SetAppVersionCode(abilityContext->GetApplicationInfo()->versionCode);
AceApplicationInfo::GetInstance().SetUid(IPCSkeleton::GetCallingUid());
AceApplicationInfo::GetInstance().SetPid(IPCSkeleton::GetCallingPid());
ImageCache::SetImageCacheFilePath(cacheDir);
ImageCache::SetCacheFileInfo();
AceEngine::InitJsDumpHeadSignal();
});
- 添加监听器:添加各种Listener
window->RegisterWindowChangeListener(aceWindowListener);
// register drag event callback
window->RegisterDragListener(aceWindowListener);
// register Occupied Area callback
window->RegisterOccupiedAreaChangeListener(aceWindowListener);
// register ace ability handler callback
window->SetAceAbilityHandler(aceWindowListener);
// register input consumer callback
std::shared_ptr<AceWindowListener> aceInputConsumer = std::make_shared<AceWindowListener>(self);
window->SetInputEventConsumer(aceInputConsumer);
- 获取默认参数:
auto defaultDisplay = Rosen::DisplayManager::GetInstance().GetDefaultDisplay();
- 获取资源管理器信息:
auto resourceManager = GetResourceManager();
- 获取各种配置
auto packagePathStr = GetBundleCodePath();
auto moduleInfo = GetHapModuleInfo();
- 将各种Message赋给Ability
Containter和View初始化
Container初始化
ace_ability.cpp
Platform::AceContainer::CreateContainer(abilityId_, frontendType, srcPath, shared_from_this(),
std::make_unique<AcePlatformEventCallback>([this]() { TerminateAbility(); },
[this](const std::string& address) {
AAFwk::Want want;
want.AddEntity(Want::ENTITY_BROWSER);
want.SetUri(address);
want.SetAction(ACTION_VIEWDATA);
this->StartAbility(want);
}),
false, useNewPipe);
- 将Container加入到Ace引擎当中
AceEngine::Get().AddContainer(instanceId, aceContainer);
View初始化
ace_ability.cpp
Platform::AceContainer::SetView(aceView, density_, 0, 0, window, callback);
pipeline初始化
ace_ability.cpp
在Ability初始化后便开始进行
auto apiCompatibleVersion = abilityContext->GetApplicationInfo()->apiCompatibleVersion;
auto apiReleaseType = abilityContext->GetApplicationInfo()->apiReleaseType;
auto apiTargetVersion = abilityContext->GetApplicationInfo()->apiTargetVersion;
auto useNewPipe = AceNewPipeJudgement::QueryAceNewPipeEnabledFA(
AceApplicationInfo::GetInstance().GetPackageName(), apiCompatibleVersion, apiTargetVersion, apiReleaseType);
LOGI("AceAbility: apiCompatibleVersion: %{public}d, apiTargetVersion: %{public}d, and apiReleaseType: %{public}s, "
"useNewPipe: %{public}d",
apiCompatibleVersion, apiTargetVersion, apiReleaseType.c_str(), useNewPipe);
之后在ace_container.cpp中将其置为true,开始使用
//ace_container.cpp
if (useNewPipeline) {
SetUseNewPipeline();
}
//container.h
void SetUseNewPipeline()
{
useNewPipeline_ = true;
}
rootNode根节点初始化
ace_container.cpp
RefPtr<NG::FrameNode> rootNode = context->GetRootElement();
rootNode->UpdateConfigurationUpdate(configurationChange);
页面加载
原始文件
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Stack() {
}.width('80%').height('80%')
.backgroundColor('#fff111')
}
}
执行页面文件
使用IDE编译后待执行文件(entry/build/default/intermediates/assets/default/js/MainAbility/pages/index.js)
var _ff3b25f72d479da6a3314472b8401bae;
/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!*******************************************************************************************************************!*\
!*** ../../../../../../DevEcoStudioProjects/MyApplication17/entry/src/main/ets/MainAbility/pages/index.ets?entry ***!
\*******************************************************************************************************************/
class Index extends View {
constructor(compilerAssignedUniqueChildId, parent, params) {
super(compilerAssignedUniqueChildId, parent);
this.__message = new ObservedPropertySimple('Hello World', this, "message");
this.updateWithValueParams(params);
}
updateWithValueParams(params) {
if (params.message !== undefined) {
this.message = params.message;
}
}
aboutToBeDeleted() {
this.__message.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id());
}
get message() {
return this.__message.get();
}
set message(newValue) {
this.__message.set(newValue);
}
render() {
Stack.create();
Stack.width('80%');
Stack.height('80%');
Stack.backgroundColor('#fff111');
Stack.pop();
}
}
loadDocument(new Index("1", undefined, {}));
_ff3b25f72d479da6a3314472b8401bae = __webpack_exports__;
/******/ })()
;
//# sourceMappingURL=index.js.map
生成页面节点树
执行JS页面文件时,会执行上面的全局方法loadDocument,在loadDocument函数中会创建相应的UI对象进行渲染加载。
- loadDocument全局函数,在C++侧通过JS引擎注入相关定义(foundation/arkui/ace_engine/frameworks/bridge/declarative_frontend/engine/jsi/jsi_view_register.cpp)
void JsRegisterViews(BindingTarget globalObj)
{
auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
if (!runtime) {
LOGE("JsRegisterViews can't find runtime");
}
auto vm = runtime->GetEcmaVm();
globalObj->Set(vm, panda::StringRef::NewFromUtf8(vm, "loadDocument"),
panda::FunctionRef::New(const_cast<panda::EcmaVM*>(vm), JsLoadDocument));
...
}
panda::Local<panda::JSValueRef> JsLoadDocument(panda::JsiRuntimeCallInfo *runtimeCallInfo)
{
LOGI("Load Document start");
...
JSView* view = static_cast<JSView*>(obj->GetNativePointerField(0));
...
if (Container::IsCurrentUseNewPipeline()) {
auto pageRootNode = view->CreateNode();
page->SetRootNode(pageRootNode);
}
...
}
页面节点树挂载
在执行JsLoadDocument方法时,入参为new Index(“1”, undefined, {})对象,该对象由于在C++侧注入了基类的定义,会走ConstructorCallback回调,生成JSView的对象并保存Render方法,JSView* view = static_cast<JSView*>(obj->GetNativePointerField(0))调用在通过JS引擎的能力从对象中获取我们生成的JSView对象,并调用JSView的对象创建page节点。
void JSView::JSBind(BindingTarget object)
{
JSClass<JSView>::Declare("NativeView");
...
JSClass<JSView>::Inherit<JSViewAbstract>();
JSClass<JSView>::Bind(object, ConstructorCallback, DestructorCallback);
}
void JSView::ConstructorCallback(const JSCallbackInfo& info)
{
JSRef<JSObject> thisObj = info.This();
JSRef<JSVal> renderFunc = thisObj->GetProperty("render");
if (!renderFunc->IsFunction()) {
LOGE("View derived classes must provide render(){...} function");
JSException::Throw("%s", "View derived classes must provide render(){...} function");
return;
}
int argc = info.Length();
if (argc > 1 && (info[0]->IsNumber() || info[0]->IsString())) {
std::string viewId = info[0]->ToString();
auto instance = AceType::MakeRefPtr<JSView>(viewId, info.This(), JSRef<JSFunc>::Cast(renderFunc));
...
LOGD("JSView ConstructorCallback: %{public}s", instance->id_.c_str());
} else {
LOGE("JSView creation with invalid arguments.");
JSException::Throw("%s", "JSView creation with invalid arguments.");
}
}
内部Render方法(RefPtrNG::FrameNode JSView::CreateNode())会在页面布局加载时进行对应,而这些JS的静态方法,包括: Stack.create();Stack.width(‘80%’);Stack.height(‘80%’);Stack.backgroundColor(‘#fff111’);Stack.pop(),在C++侧通过JS引擎注入定义,在执行JS代码时会分别走到对应的C++侧绑定方法来生成节点并设置属性。
void JSStack::JSBind(BindingTarget globalObj)
{
JSClass<JSStack>::Declare("Stack");
MethodOptions opt = MethodOptions::NONE;
JSClass<JSStack>::StaticMethod("create", &JSStack::Create, opt);
...
JSClass<JSStack>::StaticMethod("width", SetWidth);
JSClass<JSStack>::StaticMethod("height", SetHeight);
...
JSClass<JSStack>::Bind<>(globalObj);
}
- 执行JS代码时会走到对应C++侧绑定方法来创建相应的CustomNode的页面节点;
- 执行完JS代码后,会调用StageManager对象将页面创建的节点挂载到对应的根节点上;
- 根节点挂载页面节点后,会标记根节点为脏节点保存在UiTaskScheduler对象中;
- UiTaskScheduler对象收到FlushLayout指令后开始创建布局和渲染任务。
布局渲染
- Root节点标记为脏节点后,UiTaskScheduler会通过FlushLayout的方法调用Root节点的CreateLayoutTask任务
- Root节点的LayoutTask任务会遍历子节点创建相应的LayoutWrapper布局包装任务;
- LayoutWrapper布局包装任务会执行相关测算和布局任务;
在测算任务中,Index的CustomNode节点创建的LayoutWrapper布局包装器中携带了RenderFunction方法(对应页面Index对象中的render()函数),该方法执行后会生成Stack节点; - 生成的Stack节点通过Index的LayoutWrapper包装器可以找到对应的Index的CustomNode节点,并将Stack节点挂载到Index的节点下;
- CustomNode的LayoutWrapper创建完Stack节点后,会调用Stack节点的CreateLayoutWrapper方法创建对应的LayoutWrapper用于测算Stack节点的大小;
- 布局任务完成后,LayoutWrapper树会根据内部引用的节点对象指针找到各自的Node节点并将计算完成后的大小位置信息赋值到Node节点;
- Node节点检测到大小位置变更后,会创建相应的RenderWrapper绘制任务进行内容刷新绘制。