# 18.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)。

# 18.1.1:项目结构详解

为了减少不必要的干扰,笔者新建一个 OpenHarmony 项目,API 版本选择 8pages 下生成的 index.ets 文件不做任何修改,项目结构如下图所示:

18_1_1_1

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

18_1_1_2

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

18_1_1_3

由截图可知,index.abc 的文件头为 PANDA,然后是一推看不懂的乱码,中间一整段是源码编译后的内容,根据编译后的内容,可以得到的信息如下:

  • Index 继承自 View
  • @State 修饰的 message 属性被编译成了 ObservedPropertySimple 对象,后续对 message 的读写委托给了 ObservedPropertySimple 执行。
  • build() 方法去掉了,反而添加了 render() 方法,该方法内部直接调用组件的创建等方法,其中 create() 方法和 pop() 是首尾呼应的。
  • aboutToBeDeleted() 方法内部调用了 ObservedPropertySimpleaboutToBeDeleted() 方法和 SubscriberManagerdelete() 方法。
  • 末尾添加了一个 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": ""
}
1
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
1
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.txtmodule_collection.txtmanifest.json,它们是在打包的时候新添加的。

component_collection.txt 的文件内容如下:

Row,Column,Text
1

文件内容是用 "," 分割的三个组件,而这三个组件又是我们在 index.est 中使用的,再结合当前的文件名称,可以大致猜测它的作用就是 IDE 在编译期间搜集当前 Ability 目录下使用了哪些组件,然后在应用启动时引擎通过读取当前内容,然后按需加载对应的组件,从而达到节省内存开销的目的,笔者后续源码分析里也会讲到这点。

module_collection.txt 的文件内容如下:

NULL
1

由于项目里边没有添加额外的 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
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

manifest.json 各字段定义笔者注释的很清楚,重点是 mode 的注释,引擎会根据该字段的配置选取不同的 FrontendFrontend 表示代码的执行环境,目前为止 ACE 内部定义了四种 Frontend,它们分别是 JsFrontendPluginFrontendCardFrontendDeclarativeFrontend,后续我们会详细讲解这块内容。

以上就是 hap 包文件的大致介绍,了解这些基本信息有助于后续我们对源码的分析。

# 18.1.2:hap 包安装

经过签名的 hap 就可以直接运行在设备上了,安装在设备可以使用命令 hdc_std install 命令,具体用法可以参照官网:https://gitee.com/openharmony/developtools_hdc (opens new window),由于笔者条件有限,暂时没有设备,这块演示就不在做介绍了,等后续笔者有设备了再把这块的内容补充上。

# 18.1.3:小结

本节简单介绍了 hap 包内的文件结构,接下来笔者根据当前 hap 包内容结构,讲解一下 Ability 的启动过程,在这个过程中会把组件的创建流程,测量过程以及最后的绘制,页面刷新,事件机制等各大模块都统一做下讲解,由于笔者只是在业余时间做的源码分析,这部分耗时可能比较长,也欢迎感兴趣的小伙伴加入 QQ 群:695438501 (opens new window) 一起学习交流。

(adsbygoogle = window.adsbygoogle || []).push({});
请作者喝杯咖啡

津公网安备 12011402001367号

津ICP备2020008934号-2