装修器背面的秘密
学习完鸿蒙开发第一课,我兴高采烈的把一个个比如写出来后,一个个绚丽多彩的ui展示在屏幕上。依靠着ArkTS 供给的装修器,比如Component等,咱们可以便利界说好各种声明式的写法。装修器!这么一个奇特的东西,背面必定是离不开编译器的“加工”,把TS本身的写法进行填充。比如Compsoe中的Composable ,其实便是离不开kotlin 编译器的背面“加工”,才得以让咱们如此便利使用。当第一次接触ArkTS的装修器概念的时分,我就猜到,这必定也是编译器的“魔法”。
下面让咱们经过反编译,破解这一层面纱。
探究动身
咱们以一个比如动身,从官方的demo开端,咱们增加一个自界说的试图
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import errorManager from '@ohos.app.ability.errorManager';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
this.registerErrorObserver1();
this.registerErrorObserver2();
throw URIError
}
onDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
onWindowStageDestroy() {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
private registerErrorObserver1() {
errorManager.on("error", {
onUnhandledException(error: string) {
hilog.error(0x0000, 'hello',"registerErrorObserver1:"+error)
}
})
}
private registerErrorObserver2() {
errorManager.on("error", {
onUnhandledException(error: string) {
hilog.error(0x0000, 'hello',"registerErrorObserver2: "+error)
}
})
}
onForeground() {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground() {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
};
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
RelativeContainer() {
Text("text1")
.fontSize(25)
.id("text1")
.textAlign(TextAlign.Center)
.backgroundColor("#ccaabb")
.alignRules({
top:{
anchor:"text2",
align:VerticalAlign.Top
}
})
Text("text2")
.fontSize(25)
.textAlign(TextAlign.Center)
.backgroundColor("#bbccaa")
.alignRules({
// 以text1 为参考系,顶部对齐
top:{
anchor:"text1",
align:VerticalAlign.Top
}
})
}
.width('100%')
.height(200)
.backgroundColor("#111111")
}
.width('100%')
}
.height('100%')
}
}
当咱们进行编译的时分,可以在output路径下得到一个.hap后缀安装包
对hap进行解包
许多小伙伴对hap包非常陌生,其实跟android apk原理是类似的,都是从zip文件的变种而来,此时咱们只需要把后缀改为zip,然后进行解压缩,咱们就能看到里边的东西。
此时,咱们得到了要害的产品,便是modules.abc
方舟字节码abc
方舟字节码(ArkCompiler Bytecode)文件,是ArkCompiler的编译东西链以源代码作为输入编译生成的产品,其文件后缀名为.abc。也便是说,咱们得到的modules.abc,其实是运转在鸿蒙中的字节码(初始形状)。
也便是说,假如咱们可以对abc文件进行解析,比如像dex文件一样进行格式解析,那么咱们是可以得到一切的运转时信息的。惋惜的是,abc字节码目前资料比较少,要害的段界说还在改变,可是熟悉编译的咱们都知道。代码的text段,无论是dex或者是abc,都是应该也原型记载的。假如咱们能用一种东西直接解析abc文件的string字段,那么咱们就可以看到实在的源码。
这儿只需要对16进行进行字符解析的任何东西,都是可以的,我这儿推荐010Editor,经过解析modules.abc,一起采取16进行解析后,咱们可以得到以下代码
没错!这便是代码的“真面目”! 由于abc 经过ArkCompiler 解析后,其实便是TS的实在代码
了解Component
当咱们用@Component润饰一个struct的时分,经过ArkCompiler编译后,其实会生成一个类,这个承继于ViewPU。这便是一切鸿蒙组件的基类,它承担着ui刷新,localstore存储更新等要害逻辑。
ViewPU界说在ArkTS framework arkui_ace_engine傍边,是Openharmony中UI承载的要害类,其要害烘托逻辑在C++中(今后会讲到)。ViewPU 界说在pu_view.ts文件中,咱们来看一下
咱们本章不介绍ViewPU的具体内容,留到下一章节中,先看一下结构函数
constructor(parent: ViewPU, localStorage: LocalStorage, elmtId : number = -1) {
super();
结构特有id
this.id_= elmtId == -1 ? SubscriberManager.MakeId() : elmtId;
this.providedVars_ = parent ? new Map(parent.providedVars_)
: new Map<string, ObservedPropertyAbstractPU<any>>();
this.localStoragebackStore_ = undefined;
// 设置parent的关系
stateMgmtConsole.log(`ViewPU constructor: Creating @Component '${this.constructor.name}' from parent '${parent?.constructor.name}}'`);
if (parent) {
// this View is not a top-level View
this.setCardId(parent.getCardId());
// Call below will set this.parent_ to parent as well
parent.addChild(this);
} else if (localStorage) {
this.localStorage_ = localStorage;
stateMgmtConsole.debug(`${this.debugInfo()}: constructor: Using LocalStorage instance provided via @Entry.`);
}
增加订阅
SubscriberManager.Add(this);
stateMgmtConsole.debug(`${this.debugInfo()}: constructor: done`);
}
ViewPU的概念其实很简略,它负责调用SubscriberManager增加自身,后边state的回调会进行callback,一起它也有父子组件的概念,ViewPU有一个parent属性,代表当前的父组件,父子双方共用同一个localStorage,其实便是经过结构函数保证的。
一起结构进程,每一个ViewPU会结构一个特有的id,用作组件的区分,后续刷新逻辑会用到。咱们开胃菜就先到这儿完毕。
完毕
到这儿,本章先简略介绍,如何经过反编译检查abc字节码的内容,经过反编译后的类,咱们可以窥视鸿蒙运转进程的真相。学习源码可以帮开发者快速了解体系背面的运转逻辑,也便利咱们后续定制修改。当然,在不影响Android内容输出的一起,我后续将会发布更多ArkTS的内容,让咱们了解鸿蒙运转的机制,bye~