HarmonyOS Developer DevEco Studio使用指南-编译构建
HAP唯一性校验逻辑
HAP是应用安装的基本单位,在DevEco Studio工程目录中,一个HAP对应一个Module。应用打包时,每个Module生成一个.hap文件。
应用如果包含多个Module,在应用市场上架时,会将多个.hap文件打包成一个.app文件(称为Bundle),但在云端分发和端侧安装时,仍然是以HAP为基本单位。
为了能够正常分发和安装应用,需要保证一个应用安装到设备时,Module的名称、Ability的名称不重复,并且只有一个Entry类型的Module与目标设备相对应。
DevEco Studio会在编译构建时,对HAP进行上述唯一性校验,如果校验不通过,将会编译失败或给出告警。
说明
当前仅在API 8的工程中,针对distrofilter进行打包校验。
Module校验逻辑
校验目的:同一目标设备上Module唯一。
- 校验Module的Name。如果多个Module的Name不同,则校验通过。如果Name相同,继续校验deviceType。
- 校验设备类型deviceType。如果deviceType不相交,则校验通过。如果deviceType相交,继续校验distroFilter。deviceType不相交是指两个Module的deviceType中配置了完全不同的设备,例如:
//Module1和Module2配置了完全不同的设备,deviceType不相交。
//Module1
{
"deviceType": ["TV", "tablet"]
}
//Module2
{
"deviceType": ["car", "router"]
}
deviceType相交是指两个Module的deviceType中包含了相同的设备,例如:
//Module1和Module2因为都包含“tablet”设备,导致deviceType相交。
//Module1
{
"deviceType": ["TV", "tablet"]
}
//Module2
{
"deviceType": ["car", "tablet"]
}
- 校验分发规则distroFilter。如果distroFilter不相交,则校验通过。如果distroFilter相交,则无法保证Module唯一性,校验失败,打包失败。
distroFilter中包含属性apiVersion、screenShape、screenWindow、screenDensity和countryCode。相交的相关含义如下:
- distroFilter不相交:如果两个distroFilter中任意一个属性不相交,则两个distroFilter不相交。
- distroFilter相交:如果两个distroFilter中所有属性都相交,则两个distroFilter相交。
例如,两个Module中的apiVersion、screenShape、screenWindow、screenDensity都相交,但countryCode不相交,则可以区分两个Module,校验通过。
//Module1和Module2的两个distroFilter中,countryCode不相交,则两个distroFilter不相交。
//Module1
{
"distroFilter": {
"apiVersion" : {
"policy": "include",
"value": [8,9]
},
"screenShape": {
"policy": "include",
"value": ["rect"]
},
"screenWindow": {
"policy": "include",
"value": ["454*454", "466*466"]
},
"screenDensity": {
"policy": "include",
"value": ["ldpi", "xldpi"]
},
"countryCode": {
"policy": "include",
"value": ["CN", "HK"]
}
}
}
//Module2
{
"distroFilter": {
"apiVersion" : {
"policy": "include",
"value": [8,9]
},
"screenShape": {
"policy": "include",
"value": ["rect"]
},
"screenWindow": {
"policy": "include",
"value": ["454*454", "466*466"]
},
"screenDensity": {
"policy": "include",
"value": ["ldpi", "xldpi"]
},
"countryCode": {
"policy": "include",
"value": ["USA", "UK"]
}
}
}
Ability校验逻辑
校验目的:同一目标设备上Ability唯一。
- 校验Ability的Name。如果多个Ability的Name不同,则校验通过。如果Name相同,继续校验Ability所属Module的deviceType。
- 校验Ability所属Module的deviceType。如果deviceType不相交,校验通过。如果deviceType相交,继续校验Ability所属Module的distroFilter。例如,两个Ability的Name相同,但其所属Module的deviceType不相交,校验通过。
//Ability1和Ability2虽然名称相同,但由于其所属Module的deviceType不相交,所以可以区分两个Ability,校验通过。
//Ability1
{
"module": {
"name": "module_sample1",
"deviceType": ["TV", "tablet"],
"abilities": [
{
"name": "ability_sample"
}
]
}
}
//Ability2
{
"module": {
"name": "module_sample2",
"deviceType": ["car", "router"],
"abilities": [
{
"name": "ability_sample"
}
]
}
}
- 校验Ability所属Module的distroFilter。如果distroFilter不相交,校验通过。如果distroFilter相交,校验失败,抛出告警。
例如,两个Ability的Name相同,其所属Module的deviceType也相交,但其所属Module的distroFilter不相交,校验通过。
//Ability1和Ability2的Name相同,而且其所属Module的deviceType相交,但其所属Module的DistroFilter不相交,所以可以区分两个Ability,校验通过。
//Ability1
{
"module": {
"name": "module_sample",
"deviceType": ["TV", "tablet"],
"metadata": [
{
"name": "distroFilter_config"
"resource": "$profile:distroFilter_config_sample1"
}
],
"abilities": [
{
"name": "ability_sample"
}
]
}
}
//Ability1所属Module的distroFilter
{
"distroFilter": {
"apiVersion" : {
"policy": "include",
"value": [8,9]
},
"screenShape": {
"policy": "include",
"value": ["rect"]
},
"screenWindow": {
"policy": "include",
"value": ["454*454", "466*466"]
},
"screenDensity": {
"policy": "include",
"value": ["ldpi", "xldpi"]
},
"countryCode": {
"policy": "include",
"value": ["CN", "HK"]
}
}
}
//Ability2
{
"module": {
"name": "module_sample2",
"deviceType": ["TV", "tablet"],
"metadata": [
{
"name": "distroFilter_config"
"resource": "$profile:distroFilter_config_sample2"
}
],
"abilities": [
{
"name": "ability_sample"
}
]
}
}
//Ability2所属Module的distroFilter
{
"distroFilter": {
"apiVersion" : {
"policy": "include",
"value": [8,9]
},
"screenShape": {
"policy": "include",
"value": ["rect"]
},
"screenWindow": {
"policy": "include",
"value": ["454*454", "466*466"]
},
"screenDensity": {
"policy": "include",
"value": ["ldpi", "xldpi"]
},
"countryCode": {
"policy": "include",
"value": ["USA", "UK"]
}
}
}
Entry校验逻辑
校验目的:目标设备只有一个Entry类型的Module与之对应,Feature类型的Module经过deviceType及distroFilter指明的目标设备都需要存在Entry类型的Module。
- 校验Feature类型的Module经过deviceType及distroFilter指明的目标设备都存在Entry类型的Module。例如,Bundle中存在一个Entry类型Module1,其支持设备为tablet和wearable,其分发规则为circle和rect形状的屏幕,同时存在一个Feature类型的Module2,通过分发规则可知,其可以分发到rect形状的tablet和wearable设备上,而rect形状的tablet和wearable设备上存在Entry类型的Module1,校验通过。
//Entry类型Module1
{
"module": {
"name": "module_sample1",
"type": "entry",
"deviceType": ["tablet", "wearable"],
"metadata": [
{
"name": "distroFilter_config",
"resource": "$profile:distroFilter_config1"
}
]
}
}
//Module1的distroFilter,distroFilter_config1.json
{
"screenShape":{
"policy": "include",
"value": ["circle", "rect"]
}
}
//Feature类型Module2
{
"module": {
"name": "module_sample2",
"type": "feature",
"deviceType": ["tablet", "wearable"],
"metadata": [
{
"name": "distroFilter_config",
"resource": "$profile:distroFilter_config2"
}
]
}
}
//Module2的distroFilter,distroFilter_config2.json
{
"screenShape":{
"policy": "include",
"value": ["rect"]
}
}
- 校验目标设备只有一个Entry类型的Module与之对应。
a. 校验Entry类型Module的deviceType。如果deviceType不相交,校验通过。如果deviceType相交,继续校验Entry类型Module的distroFilter。
例如,同一个Bundle中存在两个Entry类型的Module,分别为Module1和Module2,两者的deviceType不相交,可以有效区分两个Module,校验通过。
//Entry类型Module1
{
"module": {
"name": "module_sample1",
"type": "entry",
"deviceType": ["tablet"]
}
}
//Entry类型Module2
{
"module": {
name: "module_sample2",
"type": "entry",
"deviceType": ["wearable"]
}
}
b. 校验Entry类型Module的distroFilter。如果distroFilter不相交,校验通过。如果distroFilter相交,校验失败,打包失败。
例如,同一个Bundle中存在两个Entry类型的Module,分别为Module1和Module2,两者的deviceType相交,但两者的distroFilter不相交,可以有效区分两个Module,校验通过。
//Entry类型Module1
{
"module": {
"name": "module_sample1",
"type": "entry",
"deviceType": ["wearable"],
"metadata": [
{
"name":"distroFilter_config"
"resource": "$profile:distroFilter_sample1"
}
]
}
}
//Module1的distroFilter,distroFilter_sample1.json
{
"distroFilter": {
"screenShape":{
"policy": "include",
"value": ["rect"]
}
}
}
//Entry类型Module1
{
"module": {
"name": "module_sample2",
"type": "entry",
"deviceType": ["wearable"],
"metadata": [
{
"name":"distroFilter_config"
"resource": "$profile:distroFilter_sample2"
}
]
}
}
//Module2的distroFilter,distroFilter_sample2.json
{
"distroFilter": {
"screenShape":{
"policy": "include",
"value": ["circle"]
}
}
}
通过命令行方式构建应用或服务
说明
该功能适用于构建API Version 4至7的HarmonyOS应用/服务。
除了使用DevEco Studio一键式构建HarmonyOS应用或原子化服务(简称应用或服务)外,还可以使用命令行工具来调用gradle任务进行构建。通过命令行的方式构建应用或服务,可用于构筑CI(Continuous Integration)持续集成流水线,按照计划时间自动化的构建HAP、APP、签名、安装部署等操作,降低开发者负担。
通过命令行方式构建应用或服务,可在Windows、Ubuntu和macOS下调用相应命令来执行,本文将以Ubuntu系统为例进行讲解,包括准备构建环境、构建HAP、构建APP、签名等操作。Windows和macOS系统,仅在搭建构建环境上存在差异,其余调用的命令行任务与Ubuntu系统没有区别。
下表是命令行构建HarmonyOS应用或服务依赖的工具版本配套表信息。
工具 | 版本要求 |
JDK | JDK 11 |
Node.js | v14.19.1及以上,且只支持14系列版本 |
Gradle | 7.3 |
sdkmgr | 1.0.0.300及以上版本 |
编译构建插件 | 3.0.5.2及以上版本 |
准备HAP/APP构建环境
安装JDK
- 下载JDK,只支持JDK 11版本。
- 进入JDK软件包目录,执行如下命令,解压已经下载好的安装包,其中jdk_x64_linux_11.0.13_8.tar.gz为软件包名称,请根据实际进行修改。
tar -xvf jdk_x64_linux_11.0.13_8.tar.gz
- 配置JDK环境变量。
a. 执行如下命令,打开当前用户下的配置文件.bashrc。
vim ~/.bashrc
b. 在配置文件中,添加JDK的环境变量信息,文件目录和软件包名称请根据实际情况进行修改。
#jdk
export JAVA_HOME=/opt/jdk11.0.13_8
export PATH=$PATH:$JAVA_HOME/bin
- 执行source命令使配置文件立即生效。
source ~/.bashrc
- 执行如下命令,检查JDK安装结果。
java -version
安装node.js
- 访问Node.js官方网站,下载Node.js,版本为v14.19.1及以上,且只支持14系列版本。
- 进入Node.js软件包目录,执行如下命令,解压已经下载好的安装包,其中node-v14.19.1-linux-x64.tar.xz为软件包名称,请根据实际进行修改。
tar -xvf node-v14.19.1-linux-x64.tar.xz
- 配置Node.js环境变量。
a. 执行如下命令,打开当前用户下的配置文件.bashrc。
vim ~/.bashrc
b. 在配置文件中,添加Node.js的环境变量信息,文件目录和软件包名称请根据实际情况进行修改。
#nodejs
export NODE_HOME=/opt/node-v14.19.1-linux-x64
export PATH=$NODE_HOME/bin:$PATH
- 执行source命令使配置文件立即生效。
source ~/.bashrc
- 执行如下命令,检查Node.js安装结果。
node -v
下载HarmonyOS SDK
通过sdkmgr命令行工具来统一下载和管理HarmonyOS SDK。
- 下载HarmonyOS SDK命令行工具:command-line-tools.zip
- 执行如下命令,解压命令行工具,工具名称请根据实际情况进行修改。
unzip command-line-tools.zip
- 执行如下命令,打开SDK工具配置文件config.properties,并设置HarmonyOS SDK存储路径,请注意,指定HarmonyOS路径时请使用绝对路径。
说明
HarmonyOS SDK存储目录请提前创建好,并为当前用户赋予写权限。
vim command-line-tools/sdkmanager/conf/config.properties
- 进入到command-line-tools/sdkmanager/bin下,安装HarmonyOS SDK。例如,同时安装Java和Js API 6的SDK、toolchains。关于sdkmgr的详细命令说明,请参考
命令行工具使用说明。
./sdkmgr install java:6 js:6 toolchains --accept-license
配置环境变量
配置HDC命令行工具和HarmonyOS SDK以及的环境变量信息。其中:
- HDC命令行工具用于HarmonyOS应用/服务调试所需的工具,该工具存放在SDK的toolchains目录中。为方便使用HDC命令行工具,请将其添加到环境变量中。
- HarmonyOS SDK中包含JS、Java、ArkTS及Native SDK(C/C++),如果工程中涉及C/C++,还需要单独设置Native SDK环境变量。
- 执行如下命令,打开当前用户下的配置文件.bashrc。
vim ~/.bashrc
- 在配置文件中,添加HDC工具、HarmonyOS SDK和Native SDK的环境变量信息,文件目录和软件包名称请根据实际情况进行修改。
#HDC工具环境变量
export HDC_HOME=/opt/HarmonyOS/SDK/toolchains
export PATH=$PATH:$HDC_HOME
#如果工程中涉及C/C++,才需要设置,需要注意的是,设置Native环境变量的路径,需要设置到版本号层级。
export OHOS_NATIVE_HOME=/opt/HarmonyOS/SDK/native/2.1.1.21
#编译构建插件版本是3.0.3.2及后续版本,请配置如下环境变量
export HOS_SDK_HOME=/opt/HarmonyOS/SDK
#编译构建插件版本是2.4.5.5及以前版本,请配置如下环境变量
export OHOS_SDK_HOME=/opt/HarmonyOS/SDK
- 执行source命令使配置文件立即生效。
source ~/.bashrc
检查工程中gradle版本
当前配套的gradle版本为7.3版本,在进行构建HAP或APP前,应确认工程中对应的gradle版本是否正确。检查和修改方法如下:
打开命令行工具,请在工程根目录下执行如下命令,查看distributionUrl中指引的gradle版本号。
vim gradle/wrapper/gradle-wrapper.properties
如果gradle版本不是7.3版本,请修改为7.3,gradle的Url可以设置为Web端下载路径、也可以设置为本地路径。
图1 下载路径方式
图2 本地路径方式
构建应用或服务
通过命令行工具可以构建出HAP或APP,用于应用或服务的测试、上架等。
如下图所示,一般通过CI流水线构建生成不带签名信息的APP,然后通过命令行工具将该APP拆分为HAP并对HAP进行签名(调试签名),最后将签名后的HAP部署到设备上进行测试;HAP测试完成后,可使用签名工具对APP进行签名(发布签名),用于将HarmonyOS应用发布到AGC(AppGallery Connect)或者将原子化服务发布到HAG(HUAWEI Ability Gallery)。
构建不带签名信息的APP
构建不带签名信息APP,请在工程根目录下执行如下命令:
说明
执行gradlew任务,需要具备执行权限,如果没有权限,可执行chmod u+x gradlew添加权限。
./gradlew packageReleaseApp
将APP拆分为HAP
将APP(带签名信息和不带签名信息APP均可)拆包为HAP,需要使用APP拆包工具hmos_app_unpacking_tool.jar进行拆包:
说明
也可以直接使用命令行工具构建HAP,具体请参考构建不带签名信息的HAP。
java -jar 'home/harmonyos/HarmonyOS/APP/hmos_app_unpacking_tool.jar' --mode app --app-path 'home/harmonyos/HarmonyOS/APP/harmonyos-demo.app' --out-path 'home/harmonyos/HarmonyOS/HAP' --force true --unpackapk false
关于该命令中需要修改的参数说明如下,其余参数不需要修改:
- jar:指定APP拆包工具。
- app-path:指定需要拆分的APP包。
- out-path:指定HAP存储路径,该路径不能与APP存放路径相同。
- force:取值为true,表示强制删除输出文件夹中的所有文件。
使用调试证书对HAP进行签名
通过APP拆包获得的HAP,无论APP是否签名,HAP都不会携带签名信息,因此要在真机设备上运行HAP,需要使用命令行工具对HAP进行签名。同时,需要提前申请签名所需文件,具体请参考申请调试证书。
- 下载HAP签名工具hapsigntoolv2.jar。
- 使用如下命令对HAP进行签名。
java -jar 'home/harmonyos/HarmonyOS/APP/hapsigntoolv2.jar' sign -mode localjks -privatekey harmonyos-demo -inputFile 'home/harmonyos/HarmonyOS/APP/unsign-harmonyos-demo.hap' -outputFile 'home/harmonyos/HarmonyOS/APP/sign-harmonyos-demo.hap' -signAlg SHA256withECDSA -keystore harmonyos-demo-debug.p12 -keystorepasswd ab123456 -keyaliaspasswd ab123456 -profile harmonyos-demo-debug.p7b -certpath harmonyos-demo-debug.cer -profileSigned 1
关于该命令中需要修改的参数说明如下,其余参数不需要修改:
- privatekey:密钥的别名信息,与创建密钥库文件时“Alias”取值保持一致。
- inputFile:需要签名的HAP名称。
- outputFile:签名后的HAP名称。
- keystore:密钥库文件,格式为.p12。
- keystorepasswd:密钥库密码。
- keyaliaspasswd:密钥密码。
- profile:申请的调试Profile文件,格式为.p7b。
- certpath:申请的调试证书文件,格式为.cer。
在真机设备中安装HAP
通过HDC工具将HAP推送到真机设备上进行安装,需要注意的是,推送的HAP必须是携带签名信息的,否则会导致HAP安装失败。
- 推送单个HAP的命令如下:
hdc app install signed_entry-release-rich.hap
- 推送多个HAP的命令如下:
hdc app install-multiple --hap xxx01.hap xxx02.hap xxx03.hap
使用发布证书为APP签名
HAP测试完成后便可以启动上架,将HarmonyOS应用发布到AGC或者将原子化服务发布到HAG。在上架APP前,需要使用发布证书对APP进行签名,可以使用hapsigntoolv2.jar工具对未签名的APP包进行签名。
在使用命令行工具进行APP包签名前,需要提前申请签名所需文件,具体请参考申请发布证书。
使用如下命令对APP进行签名。
java -jar 'home/harmonyos/HarmonyOS/APP/hapsigntoolv2.jar' sign -mode localjks -privatekey harmonyos-demo -inputFile 'home/harmonyos/HarmonyOS/APP/unsign-harmonyos-demo.app' -outputFile 'home/harmonyos/HarmonyOS/APP/sign-harmonyos-demo.app' -signAlg SHA256withECDSA -keystore harmonyos-demo-release.p12 -keystorepasswd ab123456 -keyaliaspasswd ab123456 -profile harmonyos-demo-release.p7b -certpath harmonyos-demo-release.cer -profileSigned 1
关于该命令中需要修改的参数说明如下,其余参数不需要修改:
- privatekey:密钥的别名信息,与创建密钥库文件时“Alias”取值保持一致。
- inputFile:需要签名的HAP名称。
- outputFile:签名后的HAP名称。
- keystore:密钥库文件,格式为.p12。
- keystorepasswd:密钥库密码。
- keyaliaspasswd:密钥密码。
- profile:申请的调试Profile文件,格式为.p7b。
- certpath:申请的调试证书文件,格式为.cer。
发布APP
使用发布证书签名后的APP可用于上架。
- HarmonyOS应用:发布到AGC(AppGallery Connect),具体请参考发布HarmonyOS应用指导。
- 原子化服务:发布到HAG(HUAWEI Ability Gallery),具体请参考发布原子化服务指导。
参考信息
构建不带签名信息的HAP
使用命令行工具,也可以直接构建出HAP,可以通过如下命令构建debug类型和release类型的HAP。如果需要构建携带签名信息的HAP,请参考构建携带签名的HAP或APP。
- 构建debug类型的HAP,请在工程根目录下执行如下命令:
./gradlew assembleDebug
- 构建release类型的HAP,请在工程根目录下执行如下命令:
./gradlew assembleRelease
构建携带签名的HAP或APP
要想直接构建出带签名信息的APP或HAP,您也可以提前将签名信息配置到工程中的build.gradle文件中,然后再调用构建命令生成APP或HAP。
使用在工程build.gradle中签名方案时,由于“storePassword”和“keyPassword”不支持明文密码,需要使用DevEco Studio签名后,将签名相关文件(包括p12、p7b、cer文件及material文件夹)拷贝至Ubuntu服务器中,再配置签名信息。
- 将签名文件拷贝到Ubuntu服务器后,打开工程级build.gradle文件,在ohos闭包中,添加如下代码块。
ohos {
signingConfigs {
debug { //签名类型,调试HAP使用debug,APP使用Release
storeFile file('storefile-file.p12')
storePassword 'storepassword'
keyAlias = 'alias'
keyPassword 'keypassword'
signAlg = 'SHA256withECDSA'
profile file('profile-file.p7b')
certpath file('certpath-file.cer')
}
}
...
}
- 使用命令行工具构建HAP或APP。
- 构建debug类型的HAP。
./gradlew assembleDebug
- 构建release类型的HAP。
./gradlew assembleRelease
- 构建APP。
./gradlew signReleaseApp