前言
目前公司和华为达成了合作,计划在鸿蒙正式发布next版本之前,上架公司APP的鸿蒙版本。方便用户升级手机到鸿蒙next之后,能正常使用公司APP,保证用户的既得利益。
最近我们计划在4月底上线公司的鸿蒙第一版APP,在开发和测试中遇到一个问题。那就是如何把开发同学的APP包直接丢给测试同学进行测试。由于这一块鸿蒙可能借鉴了iOS的经验,导致我们目前没办法直接给到测试同学一个APP,然后进行安装。因此目前我们有如下做法:
- 1、每次改动都让测试拿手机来开发同学电脑上进行打包安装测试。
- 2、测试直接安装鸿蒙开发环境,将代码拉到本地,运行指定分支的代码进行安装测试。
以上方法可想而知,对开发和测试都是极其不友好的。
因此考虑是否能进行优化这其中的流程。
鸿蒙 Next Flutter 桌面工具已开源:
开发环境
- Mac
- DevEco Studio NEXT Developer Preview2
- HarmonyOS next Developer Preview2
- java version "11.0.18" 2023-01-17 LTS
- hdc 1.2.0a
- 手机:Mate 60 (HarmonyOS NEXT Developer Preview2)
项目代码架构
方式1:单HAP+可选(多HSP)+可选(多HAR)方式2:多HAP+可选(多HSP)+可选(多HAR)
目前我们项目采用的是方式1
思考
目前鸿蒙next版本的APP想要安装到鸿蒙测试手机上,(都必须得对APP进行签名)。
HarmonyOS通过数字证书(.cer文件)和Profile文件(.p7b文件)来对应用/元服务进行管控。
因此只有签名过的HAP才能安装到设备上运行。 有以下两种方式:
- 1、将包上传到华为的AppGallery Connect,AppGallery Connect会重新解压APP,然后签名,之后你就可以在华为的应用商店进行下载安装。
- 开发者直接使用手机连接idea,使用
自动签名or手动签名进行安装
就目前来看,好像没有办法像Android一样丢一个APK给测试,或者给他一个网站,让测试下载下来直接安装。 真的就不行吗?确实不行,但可以优化。
DevEco Studio如何安装APP
然后我观察到idea是如何将HAP安装到测试手机上。在控制台我看到如下输出:
$ hdc shell aa force-stop com.byhk.app
$ hdc uninstall com.byhk.app in 669 ms
$ hdc shell mkdir data/local/tmp/e865b2e1cb8147408400c284260da55c
$ hdc file send /XXX/xxx/build/default/outputs/default/xxx-signed.hsp "data/local/tmp/e865b2e1cb8147408400c284260da55c" in 22 s 117 m
$ hdc file send /XXX/app/build/default/outputs/default/app-default-signed.hap "data/local/tmp/e865b2e1cb8147408400c284260da55c" in 1 min 50 s 580 ms
$ hdc shell bm install -p data/local/tmp/e865b2e1cb8147408400c284260da55c in 11 s 907 ms
$ hdc shell rm -rf data/local/tmp/e865b2e1cb8147408400c284260da55c
$ hdc shell aa start -a AppAbility -b com.byhk.app -D in 89 ms
简单介绍一下上面的命令作用:
hdc shell aa force-stop:强制停止APP,后面跟包名hdc uninstall:卸载APP,后面跟包名hdc shell mkdir:使用shell命令创建一个目录。hdc file send:将hap和hsp发送到这个目录中,可能有人会问,为什么没有HAR。因为HAR的代码复制到了其依赖的module里面了。hdc shell bm install -p:安装这个目录里面所有的东西hdc shell rm -rf:移除刚刚的目录hdc shell aa start -a AppAbility -b 包名 -D:启动APP
需要注意的是,send到这个目录的HAP,和HSP,都是进行了签名的。
重点:这个时候我就在想,如果我有一个已经签名的.app文件,我直接给到测试或者丢到一个网站上。并且给他写一份脚本,这个脚本将APP进行解压,然后把解压后的hap、hsp文件send到这个目录进行安装,是不是就行了。有道理。说干就干。
生成.app
这个很简单直接在DevEco Studio上进行操作。操作如下:
最后会在build目录下生成两个.app文件,如下:
其中一个签名了,一个未签名。
这儿有个坑,app签名了,但你解压之后的hap、hsp是没有进行签名的。
因此我需要对HAP和HSP分别进行签名。
对HAP、HSP进行签名
对HAP和HSP进行签名,需要java环境和鸿蒙的签名工具:hap_sign_tool.jar
hap_sign_tool.jar目录: 在${HOS_SDK_HOME}\HarmonyOS-NEXT-DP2\base\toolchains\lib,
如图:
使用如下命令对HAP进行签名。
java -jar hap-sign-tool.jar sign-app -keyAlias "key0" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "test.cer" -profileFile "test.p7b" -inFile "hap-unsigned.hap" -keystoreFile "test.p12" -outFile "result\hap-signed.hap" -keyPwd "123456" -keystorePwd "123456" -signCode "1"
关于该命令中需要修改的参数说明如下,其余参数不需要修改:
- keyAlias:密钥别名。
- appCertFile:申请的调试证书文件,格式为.cer。
- profileFile:申请的调试Profile文件,格式为.p7b。
- inFile:通过hvigor打包生成的未携带签名信息的HAP。
- keystoreFile:密钥库文件,格式为.p12。
- outFile:经过签名后生成的携带签名信息的HAP。
- keyPwd:密钥口令。
- keystorePwd:密钥库口令。
- signCode:是否开启代码签名,缺省为开启。1表示开启,0表示不开启。从NEXT Developer Preview2版本开始,需开启代码签名,否则将导致构建出的包无法安装到设备上。
脚本编写
这儿我使用的是Python语言,脚本分为:
- 签名脚本
- 安装脚本
签名脚本
1、解压.app文件
def unzip_app(app_file, signed_dir, output_dir):
print("开始解压APP")
os.makedirs(output_dir, exist_ok=True)
os.makedirs(signed_dir, exist_ok=True)
with zipfile.ZipFile(app_file, 'r') as zip_ref:
zip_ref.extractall(output_dir)
print("解压成功")
2、签名Hap、Hsp
def signed_app(extracted_dir, signed_dir):
print("开始签名APP")
sign_files(extracted_dir, signed_dir, '.hap')
sign_files(extracted_dir, signed_dir, '.hsp')
print("签名完成")
def sign_files(source_dir, target_dir, extension):
files = [f for f in os.listdir(source_dir) if f.endswith(extension)]
for file in files:
sign_file(source_dir, target_dir, file)
def sign_file(source_dir, target_dir, file):
sign_command = f"java -jar {hap_sign_tool} sign-app -keyAlias {alias} -signAlg \"SHA256withECDSA\" -mode \"localSign\" -appCertFile \"{cer_path}\" -profileFile \"{profile_file_path}\" -inFile \"{os.path.join(source_dir, file)}\" -keystoreFile \"{k12_path}\" -outFile \"{target_dir}/{file.split('.')[0]}-signed.{file.split('.')[1]}\" -keyPwd \"{pwd}\" -keystorePwd \"{pwd}\""
subprocess.run(sign_command, shell=True)
3、重新压缩生成zip包
def zip_signed_files(signed_dir):
os.makedirs(os.path.dirname(out_put_file_path), exist_ok=True)
print("开始压缩已签名文件")
with zipfile.ZipFile(out_put_file_path, 'w') as zip_ref:
for root, _, files in os.walk(signed_dir):
for file in files:
zip_ref.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), signed_dir))
print("压缩完成")
4、其他常量
app_file = "xxx/jojo-read-default-signed.app" //app地址
hap_sign_tool = "xxx/Huawei/Sdk/HarmonyOS-NEXT-DP2/base/toolchains/lib/hap-sign-tool.jar" //签名工具地址
cer_path = "xxx/app.cer" //cer地址
profile_file_path="xxx/app.p7b" //prpfile地址
k12_path = "xxx/app.p12" //.p12地址
alias = "ohos"
pwd = "123456" //密码
app_file_path = os.path.dirname(app_file)
file_name = os.path.basename(app_file)
package_name = "com.byhk.app" //包名
extracted_dir = app_file_path + "/zip"
signed_dir = app_file_path + "/signed"
out_put_file_dir = app_file_path + "/out/"
out_put_file_path = out_put_file_dir + file_name
完成以上,就会在out目录下生成一个.zip文件,这个文件就可以丢给测试了。但测试还需要将其解压send到手机上,因此还需要一个安装脚本。
安装脚本
1、解压文件
def unzip_app(app_file, output_dir):
print("开始解压APP")
os.makedirs(output_dir, exist_ok=True)
with zipfile.ZipFile(app_file, 'r') as zip_ref:
zip_ref.extractall(output_dir)
print("解压成功")
2、停止并卸载应用
def stop_and_uninstall_app(package_name):
print("停止并卸载应用")
subprocess.run(f"hdc shell aa force-stop {package_name}", shell=True)
subprocess.run(f"hdc uninstall {package_name}", shell=True)
subprocess.run("hdc shell rm -rf data/local/tmp/6927085eaa4645068468e40d6c797292", shell=True)
subprocess.run("hdc shell mkdir data/local/tmp/6927085eaa4645068468e40d6c797292", shell=True)
3、发送解压文件到手机
def send_files_to_device(extracted_dir, package_name):
print("发送文件到设备")
files = [f for f in os.listdir(extracted_dir) if f.endswith('.hap') or f.endswith('.hsp')]
for file in files:
subprocess.run(f"hdc file send {os.path.join(extracted_dir, file)} data/local/tmp/6927085eaa4645068468e40d6c797292", shell=True)
4、安装app并启动应用
def start_app(package_name):
print("启动应用")
subprocess.run(f"hdc shell bm install -p data/local/tmp/6927085eaa4645068468e40d6c797292", shell=True)
subprocess.run(f"hdc shell rm -rf data/local/tmp/6927085eaa4645068468e40d6c797292", shell=True)
subprocess.run(f"hdc shell aa start -a AppAbility -b {package_name}", shell=True)
print("启动成功")
5、其他常量
app_file = "xxx/app-signed.zip"
package_name = "com.byhk.app"
app_file_path = os.path.dirname(app_file)
extracted_dir = app_file_path + "/zip"
unzip_app_and_send_files(app_file, package_name, extracted_dir)
只需要测试在本地安装hdc命令环境和python环境,配置好zip包路径,即可运行脚本安装app。
后续计划使用flutter写一个桌面工具,包含app下载和安装。
完整脚本,放在文章末尾。有需要的自提。
以下是关于自动签名和手动签名的方法。
自动签名
这种方式,仅限于开发者自己调试使用。方便快捷。操作如下:
进入File > Project Structure... > Project > Signing Configs界面,勾选“Automatically generate signature”(如果是HarmonyOS工程,需同时勾选“Support HarmonyOS”),即可完成签名。如果未登录,请先单击Sign In进行登录,然后自动完成签名。如下:
手动签名
相比自动签名,手动签名就相对来说比较麻烦一点。官方文档:developer.huawei.com/consumer/cn…
操作流程图
准备证书请求文件
需要通过DevEco Studio来生成密钥(.p12)和证书请求文件(.csr)。
生成密钥(.p12)
- 在顶部菜单栏选择“Build > Generate Key and CSR”。
- 点击“New”
- Key store file:设置密钥库文件存储路径,并填写p12文件名。
- Password:设置密钥库密码,必须由大写字母、小写字母、数字和特殊符号中的两种以上字符的组合,长度至少为8位。请记住该密码,后续签名配置需要使用。
- Confirm password:再次输入密钥库密码。
生成证书请求文件(.csr)
- Alias:密钥的别名信息,用于标识密钥名称。请记住该别名,后续签名配置需要使用。
- Password:密钥对应的密码,与密钥库密码保持一致,无需手动输入。
- Validity:证书有效期,建议设置为25年及以上,覆盖应用/元服务的完整生命周期。
- Certificate:输入证书基本信息,如组织、城市或地区、国家码等。
点击“Next”
选择路径和输入名称及其后缀。
至此完成了。密钥(.p12)和证书文件(.csr),Android同学很熟悉。
申请调试证书
登录AppGallery Connect,选择“用户与访问”。
在左侧导航栏选择“证书管理”,进入证书管理页面,。
点击右上角“新增证书”
选择刚刚生成的csr文件。
会生成一个cer格式的证书文件,将其报错下来。
注册调试设备
在刚刚的证书管理下面,点击设备管理。
如果有多个设备,可以批量添加
申请调试的profile
1、登录AppGallery Connect,选择“我的项目”。 2. 找到您的项目,点击您创建的HarmonyOS应用/元服务。
- 选择“HarmonyOS应用 > HAP Provision Profile管理”,进入“管理HAP Provision Profile”页面,点击右上角“添加”。
至此,我们已经完成了profile的文件生成。(后缀名为.p7b),将其下载下来保存到本地。
(备注:每次新增设备,都需要重新生成调试profile文件)
目前我们电脑上有以下文件了: