OpenHarmony 可以进行两种形式(Hap和App)的打包,HAP是用于本地调试的,APP包是用于上架发布的。 根据不同的设备类型,一个APP包可以包含多个HAP包。
下面从两个角度进行分析
App
HarmonyOS应用发布形态为APP Pack(Application Package,简称APP),它是由一个或多个HAP(HarmonyOS Ability Package)包以及描述APP Pack属性的pack.info文件组成。
Hap
1、 一个HAP在工程目录中对应一个Module,它是由代码、资源、第三方库及应用配置文件组成,可以分为Entry和Feature两种类型。Entry:应用的主模块。一个APP中,对于同一设备类型必须有且只有一个entry类型的HAP,可独立安装运行。Feature:应用的动态特性模块。一个APP可以包含一个或多个feature类型的HAP,也可以不含。
2、HAP是Ability的部署包,HarmonyOS应用代码围绕Ability组件展开,它是由一个或多个Ability组成。
3、一个App可以有很多HAP。HAP可以直接在模拟器或者真机设备上运行,用于HarmonyOS应用开发阶段的调试和查看运行效果。
4、原子化服务由1个或多个HAP包组成,1个HAP包对应1个FA或1个PA。每个FA或PA均可独立运行,完成1个特定功能;1个或多个功能(对应FA或PA)完成1个特定的便捷服务。
OpenHarmony 的 hap 包本质上是一个压缩包,可以更改后缀名解压压缩包看下 hap 包里的文件结构,解压出来的文件结构如下所示:
Hap 包解压后,里边包含了一个 ets、resources两个 目录和三个配置文件。
abc 文件表示方舟字节码(ark bytecode,简称 abc),所以项目运行的时候 ark 虚拟机需要加载运行这些 abc 文件。
{
"app": {
"apiReleaseType": "Release",
"bundleName": "com.example.healthydiet",
"compileSdkType": "OpenHarmony",
"compileSdkVersion": "3.2.13.5",
"debug": true,
"distributedNotificationEnabled": true,
"icon": "$media:app_icon",
"iconId": 16777217,
"label": "$string:app_name",
"labelId": 16777216,
"minAPIVersion": 9,
"targetAPIVersion": 9,
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0"
},
"module": {
"abilities": [
{
"description": "$string:EntryAbility_desc",
"descriptionId": 16777218,
"icon": "$media:icon",
"iconId": 16777362,
"label": "$string:EntryAbility_label",
"labelId": 16777219,
"name": "EntryAbility",
"skills": [
{
"actions": [
"action.system.home"
],
"entities": [
"entity.system.home"
]
}
],
"srcEntrance": "./ets/entryability/EntryAbility.ts",
"startWindowBackground": "$color:start_window_background",
"startWindowBackgroundId": 16777286,
"startWindowIcon": "$media:icon",
"startWindowIconId": 16777362,
"visible": true
}
],
"compileMode": "esmodule",
"deliveryWithInstall": true,
"dependencies": [],
"description": "$string:module_desc",
"descriptionId": 16777262,
"deviceTypes": [
"default"
],
"installationFree": false,
"mainElement": "EntryAbility",
"metadata": [
{
"name": "ArkTSPartialUpdate",
"value": "true"
}
],
"name": "entry",
"pages": "$profile:main_pages",
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
],
"type": "entry",
"virtualMachine": "ark9.0.0.0"
}
}
module.json 是项目里的默认配置文件,只是在打包的时候添加了一些额外的默认配置,比如 distro 下添加了值为 ark 的 virtualMachine 字段,表示当前 hap 包表运行在 ark 虚拟机里等。
{
"summary": {
"app": {
"bundleName": "com.example.healthydiet",
"version": {
"code": 1000000,
"name": "1.0.0"
}
},
"modules": [
{
"mainAbility": "EntryAbility",
"deviceType": [
"default"
],
"abilities": [
{
"name": "EntryAbility",
"label": "$string:EntryAbility_label",
"visible": true
}
],
"distro": {
"moduleType": "entry",
"installationFree": false,
"deliveryWithInstall": true,
"moduleName": "entry"
},
"apiVersion": {
"compatible": 9,
"releaseType": "Release",
"target": 9
}
}
]
},
"packages": [
{
"deviceType": [
"default"
],
"moduleType": "entry",
"deliveryWithInstall": true,
"name": "entry-default"
}
]
}
pack.info用来描述和定义软件包(Package)的信息和属性。
pack.info 是关于整个软件包的信息,而 module.json 是关于单个模块的信息
可以通过 010 Editor 打开生成的字节码文件,部分内容如下所示:
class PreviewCustomCounter extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1) {
super(parent, __localStorage, elmtId);
this.__weight = new ObservedPropertySimplePU(50, this, "weight");
this.setInitiallyProvidedValue(params);
}
setInitiallyProvidedValue(params) {
if (params.weight !== undefined) {
this.weight = params.weight;
}
}
updateStateVars(params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__weight.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__weight.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
get weight() {
return this.__weight.get();
}
set weight(newValue) {
this.__weight.set(newValue);
}
initialRender() {
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Row.create();
if (!isInitialRender) {
Row.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
{
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
if (isInitialRender) {
ViewPU.create(new CustomCounter(this, {
value: this.weight + 'g',
onDec: () => {
this.weight -= 50;
},
onInc: () => {
this.weight += 50;
}
}, undefined, elmtId));
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
value: this.weight + 'g'
});
}
ViewStackProcessor.StopGetAccessRecording();
});
}
Row.pop();
}
rerender() {
this.updateDirtyElements();
}
}