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

发布时间:2023年12月26日

在这里插入图片描述

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 是关于单个模块的信息

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

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

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

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();
    }
}

在这里插入图片描述

文章来源:https://blog.csdn.net/qq_23334071/article/details/135213140
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。