【HarmonyOS开发】探究Hap与App包的结构与区别

2025-10-11 07:58:00

1、Hap与App包的区别

OpenHarmony 可以进行两种形式(Hap和App)的打包,HAP是用于本地调试的,APP包是用于上架发布的。 根据不同的设备类型,一个APP包可以包含多个HAP包。

下面从两个角度进行分析

1.1 编译构建角度

编译构建是将HarmonyOS应用的源代码、资源、第三方库等打包生成HAP或者APP的过程。其中,HAP可以直接运行在真机设备或者模拟器中;APP则是用于应用上架到华为应用市场。一个HarmonyOS工程下可以存在多个Module,在编译构建时,可以选择对单个Module进行编译构建;也可以对整个工程进行编译构建,同时生成多个HAP。

1.2 签名角度

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个特定的便捷服务。

2、探究Hap的结构

OpenHarmony 的 hap 包本质上是一个压缩包,可以更改后缀名解压压缩包看下 hap 包里的文件结构,解压出来的文件结构如下所示:

2.1 解压Hap包

2.2 Hap的目录结构

2.3 目录说明

Hap 包解压后,里边包含了一个 ets、resources两个 目录和三个配置文件。

ets:编译后的源码文件;

modules.abc:源码编译之后的方舟字节码sourceMaps.map:配置项,个人理解为是abc文件的一个索引文件;

resources:资源文件;module.json/pack.info: Hap 包的配置文件;resources.index: 资源目录的索引文件;

abc 文件表示方舟字节码(ark bytecode,简称 abc),所以项目运行的时候 ark 虚拟机需要加载运行这些 abc 文件。

2.3.1 module.json

{

"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 虚拟机里等。

2.3.2 pack.info

{

"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)的信息和属性。

2.3.3 区别

pack.info 是关于整个软件包的信息,而 module.json 是关于单个模块的信息

pack.info 是用于描述和定义整个软件包的信息,它位于软件包的根目录下,用于标识和描述软件包本身。 module.json是用于描述一个模块的信息,它位于模块的根目录下,用于定义模块的属性和功能。

3、思考鸿蒙应用的启动流程

桌面点击应用App图标;操作系统(OpenHarmony)检测到应用启动请求,并根据应用的包名和入口信息找到对应的 HAP 包路径;操作系统加载 HAP 包的信息,并解析其中的配置文件,如 pack.info、module.json等;操作系统根据配置文件中的入口信息,找到应用的入口类和入口方法;操作系统创建应用的运行环境,并执行应用的入口类和入口方法;应用的入口方法执行初始化操作,如创建应用窗口、注册事件监听器等,加载资源文件;应用初始化完成后,执行渲染页面,执行生命周期,进行网络请求等;用户关闭应用,应用关闭;

4、备注

可以通过 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();

}

}

华为g510怎么样 参数介绍:外观可选两款颜色 分辨率、像素及屏幕材质一目了然:每日快播
在哪里买比特币?2025年最全比特币购买指南