# 19.1:项目打包和安装
笔者在第 17 章节里详细介绍了 OpenHarmony 项目的签名和打包,本节再详细介绍一下打包后的 hap 包的目录结构和安装过程,目的是为接下来分析 FA 的启动做准备。
📢:由于 OpenHarmony 在快速迭代中,期间官方对 IDE 和 SDK 又做了升级,OpenHarmony 的专属 IDE 也和 HarmonyOS 合并到一块了,笔者目前使用的 IDE 版本是:DevEco Studio 3.0 Beta4(Build Version: 3.0.0.992),SDK 版本是:API 8(3.1.6.6)。
# 19.1.1:项目结构详解
为了减少不必要的干扰,笔者新建一个 OpenHarmony 项目,API 版本选择 8,pages 下生成的 index.ets
文件不做任何修改,项目结构如下图所示:

从截图看,index.ets
页面使用了 Row
、Column
和 Text
三个组件,接下来给项目添加签名配置,最后打出来一个 hap 包,hap 包解压缩后,目录结构如下图所示:

解压后的文件结构和项目源码里的文件结构一一对应,源码里的 ets 文件被编译成了对应的 abc、js 和 map 三个文件,abc 是方舟字节码文件格式,使用 010Editor 打开 index.abc 文件,内容如下所示:

由截图可知,index.abc 的文件头为 PANDA
,然后是一推看不懂的乱码,中间一整段是源码编译后的内容,根据编译后的内容,可以得到的信息如下:
Index
继承自View
。@State
修饰的message
属性被编译成了ObservedPropertySimple
对象,后续对message
的读写委托给了ObservedPropertySimple
执行。build()
方法去掉了,反而添加了render()
方法,该方法内部直接调用组件的创建等方法,其中create()
方法和pop()
是首尾呼应的。aboutToBeDeleted()
方法内部调用了ObservedPropertySimple
的aboutToBeDeleted()
方法和SubscriberManager
的delete()
方法。- 末尾添加了一个
laodDocument()
方法,参数传递的是当前Index
实例。
根据 abc 文件提供的信息,可大致明白 @State
修饰的属性改变是如何引发页面的刷新了,重点实现就是在 ObservedPropertySimple
提供的 set()
方法里(这块我们后续会着重分析),abc 文件了解完了以后,接着看 map 文件,使用 010Editor 打开 index.js.map 文件,内容如下所示:
{
"version": 3,
"file": "./pages/index.js",
"mappings": ";;;;;;MAAyB,KAAK;IAA9;KADxC;;;;;;;******QAC,OAAO,EG,OAQY;KAChB",
"sources": [
"webpack:******entry/src/main/ets/MainAbility/pages/index.ets"
],
"names": [
],
"sourceRoot": ""
}
2
3
4
5
6
7
8
9
10
11
map 文件是 json 格式,这个暂时没什么好介绍的,最后看下 index.js 文件,使用 010Editor 打开 index.js 文件,内容如下:
var _b9b2018c481fcb6ca6ab29c4140e5937;
/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!********************************************************************************************************************!*\
!*** ../../../../../../HarmonyOS_Projects/openharmony_test01/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() {
Row.create();
Row.height('100%');
Column.create();
Column.width('100%');
Text.create(this.message);
Text.fontSize(50);
Text.fontWeight(FontWeight.Bold);
Text.pop();
Column.pop();
Row.pop();
}
}
loadDocument(new Index("1", undefined, {}));
_b9b2018c481fcb6ca6ab29c4140e5937 = __webpack_exports__;
/******/ })()
;
//# sourceMappingURL=index.js.map
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
index.js 内容和 index.abc 内容主体上是一致的,它们代表的意思也是一样的,笔者就不在讲述了,接下来我们看下 MainAbility 目录下新增加的三个文件:component_collection.txt、module_collection.txt 和 manifest.json,它们是在打包的时候新添加的。
component_collection.txt 的文件内容如下:
Row,Column,Text
文件内容是用 "," 分割的三个组件,而这三个组件又是我们在 index.est
中使用的,再结合当前的文件名称,可以大致猜测它的作用就是 IDE 在编译期间搜集当前 Ability 目录下使用了哪些组件,然后在应用启动时引擎通过读取当前内容,然后按需加载对应的组件,从而达到节省内存开销的目的,笔者后续源码分析里也会讲到这点。
module_collection.txt 的文件内容如下:
NULL
由于项目里边没有添加额外的 module,所以该内容为 NULL
。
manifest.json 的文件内容如下:
{
"appID": "com.example.openharmony_test01",// 唯一的 appID
"versionName": "1.0.0", // 版本号
"versionCode": 1000000, // 版本号
"minPlatformVersion": 8, // 最小版本
"appName": ".MainAbility", // 当前 Ability 的名字
"deviceType": [ // 运行在目标设备
"default",
"tablet"
],
"window": { // 窗口信息配置
"designWidth": 720,
"autoDesignWidth": false
},
"pages": [ // 页面配置
"pages/index"
],
"mode": { // 运行模式,目的是选择不同的 Frontend
"syntax": "ets", // 语法为 ets
"type": "pageAbility" // 类型为 pageAbility
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
manifest.json 各字段定义笔者注释的很清楚,重点是 mode 的注释,引擎会根据该字段的配置选取不同的 Frontend,Frontend 表示代码的执行环境,目前为止 ACE 内部定义了四种 Frontend,它们分别是 JsFrontend
、PluginFrontend
、CardFrontend
和 DeclarativeFrontend
,后续我们会详细讲解这块内容。
以上就是 hap 包文件的大致介绍,了解这些基本信息有助于后续我们对源码的分析。
# 19.1.2:hap 包安装
经过签名的 hap 就可以直接运行在设备上了,安装在设备可以使用命令 hdc_std install 命令,具体用法可以参照官网:https://gitee.com/openharmony/developtools_hdc (opens new window),由于笔者条件有限,暂时没有设备,这块演示就不在做介绍了,等后续笔者有设备了再把这块的内容补充上。
# 19.1.3:小结
本节简单介绍了 hap 包内的文件结构,接下来笔者根据当前 hap 包内容结构,讲解一下 Ability 的启动过程,在这个过程中会把组件的创建流程,测量过程以及最后的绘制,页面刷新,事件机制等各大模块都统一做下讲解,由于笔者只是在业余时间做的源码分析,这部分耗时可能比较长,也欢迎感兴趣的小伙伴加入 QQ 群:695438501 (opens new window) 一起学习交流。
