鸿蒙ACE-状态分析-getTarget

165 阅读2分钟

getTarget接口:获取状态管理框架代理前的原始对象

developer.huawei.com/consumer/cn…

转换前

/**
 *
 * GetTargetCmp.ets
 * Created by unravel on 2024/9/10
 * @abstract
 */
import { UIUtils } from '@kit.ArkUI';

@ObservedV2
class ObservedClass {
  @Trace name: string = "Tom";
}

let globalObservedObject: ObservedClass = new ObservedClass(); // 不被代理
let globalNumberList: number[] = [1, 2, 3]; // 不被代理
let globalSampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // 不被代理
let globalSampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // 不被代理
let globalSampleDate: Date = new Date(); // 不被代理

@ComponentV2
export struct GetTargetCmp {
  @Local observedObject: ObservedClass = globalObservedObject; // V2中对象不被代理
  @Local numberList: number[] = globalNumberList; // Array类型创建代理
  @Local sampleMap: Map<number, string> = globalSampleMap; // Map类型创建代理
  @Local sampleSet: Set<number> = globalSampleSet; // Set类型创建代理
  @Local sampleDate: Date = globalSampleDate; // Date类型创建代理

  build() {
    Column() {
      Text(`this.observedObject === globalObservedObject ${this.observedObject ===
        globalObservedObject}`) // true
      Text(`UIUtils.getTarget(this.numberList) === globalNumberList: ${UIUtils.getTarget(this.numberList) ===
        globalNumberList}`) // true
      Text(`UIUtils.getTarget(this.sampleMap) === globalSampleMAP: ${UIUtils.getTarget(this.sampleMap) ===
        globalSampleMap}`) // true
      Text(`UIUtils.getTarget(this.sampleSet) === globalSampleSet: ${UIUtils.getTarget(this.sampleSet) ===
        globalSampleSet}`) // true
      Text(`UIUtils.getTarget(this.sampleDate) === globalSampleDate: ${UIUtils.getTarget(this.sampleDate) ===
        globalSampleDate}`) // true
    }
  }
}

转换后

ComponentV2中转换成了如下代码

if (!("finalizeConstruction" in ViewPU.prototype)) {
    Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
import { UIUtils } from "@ohos:arkui.StateManagement";
@ObservedV2
class ObservedClass {
    @Trace
    name: string = "Tom";
}
let globalObservedObject: ObservedClass = new ObservedClass(); // 不被代理
let globalNumberList: number[] = [1, 2, 3]; // 不被代理
let globalSampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // 不被代理
let globalSampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // 不被代理
let globalSampleDate: Date = new Date(); // 不被代理
export class GetTargetCmp extends ViewV2 {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) {
        super(parent, elmtId, extraInfo);
        this.observedObject = globalObservedObject;
        this.numberList = globalNumberList;
        this.sampleMap = globalSampleMap;
        this.sampleSet = globalSampleSet;
        this.sampleDate = globalSampleDate;
        this.finalizeConstruction();
    }
    @Local
    observedObject: ObservedClass; // V2中对象不被代理
    @Local
    numberList: number[]; // Array类型创建代理
    @Local
    sampleMap: Map<number, string>; // Map类型创建代理
    @Local
    sampleSet: Set<number>; // Set类型创建代理
    @Local
    sampleDate: Date; // Date类型创建代理
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Column.create();
        }, Column);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`this.observedObject === globalObservedObject ${this.observedObject ===
                globalObservedObject}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`UIUtils.getTarget(this.numberList) === globalNumberList: ${UIUtils.getTarget(this.numberList) ===
                globalNumberList}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`UIUtils.getTarget(this.sampleMap) === globalSampleMAP: ${UIUtils.getTarget(this.sampleMap) ===
                globalSampleMap}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`UIUtils.getTarget(this.sampleSet) === globalSampleSet: ${UIUtils.getTarget(this.sampleSet) ===
                globalSampleSet}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`UIUtils.getTarget(this.sampleDate) === globalSampleDate: ${UIUtils.getTarget(this.sampleDate) ===
                globalSampleDate}`);
        }, Text);
        Text.pop();
        Column.pop();
    }
    rerender() {
        this.updateDirtyElements();
    }
}

如果是ComponentV1,会转换成如下代码

image.png

UIUtil.getTarget

可以看到编译过后,UIUtils.getTarget没有任何改变

我们可以从 gitee.com/openharmony… 中看到这个方法的具体实现

image.png

代码里根据是否是V1版本的状态变量,进行了区分处理。除此之外,返回传入的对象

V1版本

首先判断是否是一个ObservedObject。这里只有true的时候才会进入。接着调用了 ObservedObject.GetRawObject

image.png

ObservedObject.IsObservedObject 判断的是 obj有没有ObservedObject__IS_OBSERVED_OBJECT这个属性

最终我们找到了SubscribableHandler这个类,只要是 ObservedObject__IS_OBSERVED_OBJECT就返回true

SubscribableHandler这个类其实就是最终状态变量的代理类,状态变化的观察就依靠这个类

obj作为一个状态管理,它也是被SubscribableHandler代理了get和set

image.png

同理,我们看到ObservedObject.GetRawObject实际上是读取的obj的__OBSERVED_OBJECT_RAW_OBJECT属性

这个会走到SubscribableHandler的get方法,在这个方法里判断如果是__OBSERVED_OBJECT_RAW_OBJECT属性就把target返回回去

image.png

V2版本

V2版本是获取了soruce的ObserveV2.SYMBOL_PROXY_GET_TARGET属性

和V1版本的实现很类似。只不过对于普通对象,V1版本的代理是SubscribableHandle,而V2版本的则是ObjectProxyHandler

image.png

小结

  1. 不管是V1还是V2版本的状态变量,最终都会被一个Proxy进行代理,这样对于这个对象属性的修改和访问就都能监听到了
  2. UIUtile的getTarget方法,会调用ObservedObject的GetRawObject,并最终调用到上面代理的get方法,在get方法里根据特定的属性判定,返回原有的target