其他
OpenHarmony源码系列:如何触发UI刷新?
本文作者
作者:Pika
链接:
https://juejin.cn/post/7349722583158521882
本文由作者授权发布。
注意鸿蒙系统一直在更新,源码可能会有变更,但是核心体系结构变化不会太大,依旧可以帮助大家建立对鸿蒙源码体系的认知。
OpenHarmony源码系列:
OpenHarmony源码系列: 鸿蒙页面背后的机制,打通 JS View 与C++世界
通过前两篇文章,我们知道了一个@Component 修饰的TS类是如何生成UI的具体过程。在ArkUI中,每一个用ts编写的控件,都可以在C++中找到最终的Component映射。接下来我们将继续学习,ArkUI中,如果UI发生改变时,engine是如何完成Component与Element的刷新。学习完本篇,你将会了解到@State 装饰器修饰的变量如何触发UI的刷新,同时也能够了解到Element是如何驱动元素发生更新。
@Component
struct Index {
@State isShow:Boolean = false
build() {
Row() {
if (this.isShow){
Column() {
Text("Column")
}
}else {
Row(){
Text("Row")
}
}
}
.height('100%')
.onClick((event)=>{
this.isShow = !this.isShow
})
}
}
class Index extends ViewPU {
//构造函数
constructor(parent, params, __localStorage, elmtId = -1) {
super(parent, __localStorage, elmtId);
// 创建一个ObservedPropertyObjectPU对象,它是后面UI刷新的起点,其中创建了一个隐藏变量__isShow
this.__isShow = new ObservedPropertyObjectPU(false, this, "isShow");
this.setInitiallyProvidedValue(params);
}
// 初始化isShow的值为指定的默认值false
setInitiallyProvidedValue(params) {
if (params.isShow !== undefined) {
this.isShow = params.isShow;
}
}
updateStateVars(params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__isShow.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__isShow.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
get isShow() {
return this.__isShow.get();
}
set isShow(newValue) {
this.__isShow.set(newValue);
}
initialRender() {
//observeComponentCreation 构造组件树
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Row.create();
Row.height('100%');
Row.onClick((event) => {
// 发生改变时,调用set isShow(newValue) 方法
this.isShow = !this.isShow;
});
if (!isInitialRender) {
Row.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
If.create();
if (this.isShow) {
this.ifElseBranchUpdateFunction(0, () => {
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Column.create();
if (!isInitialRender) {
Column.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Text.create("Column");
if (!isInitialRender) {
Text.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
Text.pop();
Column.pop();
});
}
else {
this.ifElseBranchUpdateFunction(1, () => {
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Row.create();
if (!isInitialRender) {
Row.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Text.create("Row");
if (!isInitialRender) {
Text.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
Text.pop();
Row.pop();
});
}
if (!isInitialRender) {
If.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
If.pop();
Row.pop();
}
rerender() {
this.updateDirtyElements();
}
}
// class definitions for backward compatibility
class ObservedPropertyObjectPU<T> extends ObservedPropertyPU<T> {
}
class ObservedPropertyPU<T> extends ObservedPropertyAbstractPU<T>
implements PeerChangeEventReceiverPU<T>, ObservedObjectEventsPUReceiver<T> {
private wrappedValue_: T;
constructor(localInitValue: T, owningView: IPropertySubscriber, propertyName: PropertyInfo) {
super(owningView, propertyName);
this.setValueInternal(localInitValue);
}
protected notifyPropertyHasChangedPU() {
stateMgmtProfiler.begin("ObservedPropertyAbstractPU.notifyPropertyHasChangedPU");
stateMgmtConsole.debug(`${this.debugInfo()}: notifyPropertyHasChangedPU.`)
if (this.owningView_) {
if (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) {
// send viewPropertyHasChanged right away
//这里的owningView_就是传入的this
this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getAllPropertyDependencies());
} else {
// mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending
this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending;
}
}
viewPropertyHasChanged(varName: PropertyInfo, dependentElmtIds: Set<number>): void {
省略前面无关代码
if (dependentElmtIds.size && !this.isFirstRender()) {
if (!this.dirtDescendantElementIds_.size && !this.runReuse_) {
// mark ComposedElement dirty when first elmtIds are added
// do not need to do this every time
this.markNeedUpdate();
}
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;
}
void JSViewPartialUpdate::JSBind(BindingTarget object)
{
JSClass<JSViewPartialUpdate>::Declare("NativeViewPartialUpdate");
MethodOptions opt = MethodOptions::NONE;
JSClass<JSViewPartialUpdate>::StaticMethod("create", &JSViewPartialUpdate::Create, opt);
JSClass<JSViewPartialUpdate>::StaticMethod("createRecycle", &JSViewPartialUpdate::CreateRecycle, opt);
JSClass<JSViewPartialUpdate>::Method("markNeedUpdate", &JSViewPartialUpdate::MarkNeedUpdate);
JSClass<JSViewPartialUpdate>::Method("syncInstanceId", &JSViewPartialUpdate::SyncInstanceId);
JSClass<JSViewPartialUpdate>::Method("restoreInstanceId", &JSViewPartialUpdate::RestoreInstanceId);
JSClass<JSViewPartialUpdate>::CustomMethod("getInstanceId", &JSViewPartialUpdate::GetInstanceId);
JSClass<JSViewPartialUpdate>::Method("markStatic", &JSViewPartialUpdate::MarkStatic);
JSClass<JSViewPartialUpdate>::Method("finishUpdateFunc", &JSViewPartialUpdate::JsFinishUpdateFunc);
JSClass<JSViewPartialUpdate>::Method("setCardId", &JSViewPartialUpdate::JsSetCardId);
JSClass<JSViewPartialUpdate>::CustomMethod("getCardId", &JSViewPartialUpdate::JsGetCardId);
JSClass<JSViewPartialUpdate>::Method("elmtIdExists", &JSViewPartialUpdate::JsElementIdExists);
JSClass<JSViewPartialUpdate>::CustomMethod("isLazyItemRender", &JSViewPartialUpdate::JSGetProxiedItemRenderState);
JSClass<JSViewPartialUpdate>::CustomMethod("isFirstRender", &JSViewPartialUpdate::IsFirstRender);
JSClass<JSViewPartialUpdate>::CustomMethod(
"findChildByIdForPreview", &JSViewPartialUpdate::FindChildByIdForPreview);
JSClass<JSViewPartialUpdate>::CustomMethod(
"resetRecycleCustomNode", &JSViewPartialUpdate::JSResetRecycleCustomNode);
JSClass<JSViewPartialUpdate>::CustomMethod(
"queryNavDestinationInfo", &JSViewPartialUpdate::JSGetNavDestinationInfo);
JSClass<JSViewPartialUpdate>::CustomMethod("getUIContext", &JSViewPartialUpdate::JSGetUIContext);
JSClass<JSViewPartialUpdate>::InheritAndBind<JSViewAbstract>(object, ConstructorCallback, DestructorCallback);
}
markNeedUpdate
void JSViewPartialUpdate::MarkNeedUpdate()
{
needsUpdate_ = ViewPartialUpdateModel::GetInstance()->MarkNeedUpdate(viewNode_);
}
bool ViewPartialUpdateModelImpl::MarkNeedUpdate(const WeakPtr<AceType>& node)
{
ACE_SCOPED_TRACE("JSView::MarkNeedUpdate");
auto weakElement = AceType::DynamicCast<ComposedElement>(node);
if (weakElement.Invalid()) {
LOGE("Invalid Element weak ref, internal error");
return false;
}
//走到这里返回值就永远是true了
auto element = weakElement.Upgrade();
if (element) {
element->MarkDirty();
}
return true;
}
auto updateFunction = [weak = AceType::WeakClaim(this)]() -> void {
auto jsView = weak.Upgrade();
CHECK_NULL_VOID(jsView);
ContainerScope scope(jsView->GetInstanceId());
if (!jsView->needsUpdate_) {
return;
}
jsView->needsUpdate_ = false;
{
ACE_SCOPED_TRACE("JSView: ExecuteRerender");
jsView->jsViewFunction_->ExecuteRerender();
}
for (const UpdateTask& updateTask : jsView->pendingUpdateTasks_) {
ViewPartialUpdateModel::GetInstance()->FlushUpdateTask(updateTask);
}
jsView->pendingUpdateTasks_.clear();
};
生成类中
rerender() {
this.updateDirtyElements();
}
// ComposedElement just maintain a child element may have render node.
class ACE_EXPORT ComposedElement : public Element {
DECLARE_ACE_TYPE(ComposedElement, Element);
// RenderElement will have a RenderNode and displays in the screen.
class ACE_EXPORT RenderElement : public Element {
DECLARE_ACE_TYPE(RenderElement, Element);
void Element::MarkDirty()
{
RefPtr<PipelineContext> context = context_.Upgrade();
if (context) {
context->AddDirtyElement(AceType::Claim(this));
MarkNeedRebuild();
}
}
Rebuild
void Element::Rebuild()
{
//判断是否需要被rebuild
if (!needRebuild_) {
return;
}
needRebuild_ = false;
// When rebuild comes, newComponent_ should not be null, and will go to these 3 steps:
// 1. Update self using new component
// 2. PerformBuild will build and update child recursively
// 3. Finish update and release the new component
Update();
PerformBuild();
SetNewComponent(nullptr);
}
void ComposedElement::Update()
{
const RefPtr<ComposedComponent> compose = AceType::DynamicCast<ComposedComponent>(component_);
if (compose != nullptr) {
name_ = compose->GetName();
SetElementId(compose->GetElementId());
if (id_ != compose->GetId()) {
auto context = context_.Upgrade();
if (addedToMap_ && context != nullptr) {
context->RemoveComposedElement(id_, AceType::Claim(this));
context->AddComposedElement(compose->GetId(), AceType::Claim(this));
}
id_ = compose->GetId();
}
compose->ClearNeedUpdate();
}
if (HasPageTransitionFunction()) {
auto pageElement = GetPageElement();
if (pageElement) {
pageElement->SetPageTransitionFunction(std::move(pageTransitionFunction_));
}
}
}
void ComposedElement::PerformBuild()
{
auto context = context_.Upgrade();
....
auto child = children_.empty() ? nullptr : children_.front();
auto composedComponent = AceType::DynamicCast<ComposedComponent>(component_);
if (composedComponent) {
auto composedChild = composedComponent->GetChild();
if (HasRenderFunction() && composedComponent->GetNeedReserveChild()) {
auto flexItem = AceType::DynamicCast<SoleChildComponent>(composedChild);
if (flexItem) {
flexItem->SetChild(component);
UpdateChild(child, flexItem);
return;
}
}
}
UpdateChild(child, component);
}
RefPtr<Element> Element::UpdateChildWithSlot(
const RefPtr<Element>& child, const RefPtr<Component>& newComponent, int32_t slot, int32_t renderSlot)
{
// Considering 4 cases:
// 1. child == null && newComponent == null --> do nothing
如果Element 为 null 并且 Component为null,则什么也不做
if (!child && !newComponent) {
return nullptr;
}
// 2. child == null && newComponent != null --> create new child configured with newComponent
新增:child == null && newComponent != null:通过Component建立对应的element
if (!child) {
auto newChild = InflateComponent(newComponent, slot, renderSlot);
ElementRegister::GetInstance()->AddElement(newChild);
return newChild;
}
// 3. child != null && newComponent == null --> remove old child
删除:child != null && newComponent == null:移除elemnt
if (!newComponent) {
ElementRegister::GetInstance()->RemoveItemSilently(child->GetElementId());
DeactivateChild(child);
return nullptr;
}
// 4. child != null && newComponent != null --> update old child with new configuration if possible(determined by
// [Element::CanUpdate]), or remove the old child and create new one configured with newComponent.
更新:child != null && newComponent != null
auto context = context_.Upgrade();
不支持更新,那么删除旧的element,添加新的element
if (!child->CanUpdate(newComponent)) {
// Can not update
auto needRebuildFocusElement = AceType::DynamicCast<Element>(GetFocusScope());
if (context && needRebuildFocusElement) {
context->AddNeedRebuildFocusElement(needRebuildFocusElement);
}
ElementRegister::GetInstance()->RemoveItemSilently(child->GetElementId());
DeactivateChild(child);
auto newChild = InflateComponent(newComponent, slot, renderSlot);
ElementRegister::GetInstance()->AddElement(newChild);
return newChild;
}
.....
能够更新
auto newChild = DoUpdateChildWithNewComponent(child, newComponent, slot, renderSlot);
if (newChild != nullptr) {
newChild->SetElementId(newComponent->GetElementId());
ElementRegister::GetInstance()->AddElement(newChild);
}
return newChild;
.....
return newChild;
}
不可更新 | 可更新 |
删除当前element树的旧element,然后通过Component生成一个新的element再加入 | 通过新的Component配置去更新element |
Row() {
if (this.isShow){
Column() {
Text("Column")
}
}else {
Row(){
Text("Row")
}
}
}
virtual void SetNewComponent(const RefPtr<Component>& newComponent)
{
component_ = newComponent;
if (newComponent) {
retakeId_ = newComponent->GetRetakeId();
componentTypeId_ = AceType::TypeId(component_);
ignoreInspector_ = newComponent->IsIgnoreInspector();
SetElementId(newComponent->GetElementId());
MarkNeedRebuild();
}
}
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!