HarmonyOS开发:HAP打包——应用打包流程
HarmonyOS开发HAP打包——应用打包流程核心要点HAP是HarmonyOS应用的基本分发单元理解从源码到HAP的完整打包链路是掌控应用构建、发布、安装全流程的基石。背景与动机你写了一堆ArkTS代码画了一堆UI界面然后呢代码躺在工程目录里用户手机上可跑不起来。你得把代码、资源、配置文件打包成一个HAP——HarmonyOS Ability Package才能装到设备上。这事儿听起来简单不就是打包嘛但真上手你会发现一堆问题打包命令怎么敲配置文件里那些字段啥意思为什么Debug包和Release包体积差这么多为什么有时候打包报签名错误为什么多模块工程打出来的包结构跟单模块不一样这些问题背后是一条从源码到HAP的完整链路。搞不懂这条链路你就只能对着报错信息干瞪眼。今天就把这条链路从头到尾捋清楚。核心原理HAP包到底是什么HAP本质上是一个ZIP压缩包后缀名是.hap里面装的是你的应用代码编译产物、资源和配置信息。把它解压开来结构长这样MyApp.hap ├── entry.json # 模块配置信息 ├── module.json # 模块元数据Ability声明、权限等 ├── resources/ # 资源文件图片、字符串、布局等 │ ├── base/ │ └── rawfile/ ├── ets/ # ArkTS编译产物 │ └── modules.abc # 方舟字节码 ├── libs/ # 原生库.so文件 └── pack.info # 包信息摘要看到没HAP不是随便打包的它有严格的目录结构规范。每个文件、每个目录都有明确的作用设备端的包管理服务就靠这些结构来解析和安装你的应用。打包流程全景图从你按下Build到HAP文件生成中间经历了什么看这张图源码工程资源编译ArkTS编译 → ABC字节码原生库编译 → .so配置文件合并与校验资源索引生成模块打包 → HAP签名最终HAP产物整个流程可以分成四大阶段编译阶段ArkTS代码编译成ABC字节码C/C代码编译成.so库资源处理阶段资源文件编译、索引生成、rawfile处理配置阶段module.json和entry.json合并校验权限声明检查打包签名阶段所有产物按目录结构组装成ZIP然后签名hvigorw打包的幕后推手DevEco Studio点Build的时候实际执行的是hvigorw命令。hvigor是HarmonyOS的构建工具类似Android的Gradle但基于TypeScript编写扩展性更强。核心命令就这几个# Debug打包hvigorw assembleHap--modemodule-pmoduleentrydefault-pproductdefault# Release打包hvigorw assembleHap--modemodule-pmoduleentrydefault-pproductdefault-pbuildTyperelease# 清理构建产物hvigorw clean你可能会问--mode module是啥-p moduleentrydefault又是啥别急后面代码实战部分会拆开讲。代码实战基础用法单模块打包配置一个最简单的Entry模块打包配置长这样// entry/src/main/module.json5{module:{name:entry,// 模块名称必须唯一type:entry,// 模块类型entry/feature/har/hspdeviceTypes:[// 支持的设备类型phone,tablet,2in1],deliveryWithInstall:true,// 是否随应用一起安装installationFree:false,// 是否支持免安装pages:$profile:main_pages,// 页面路由配置abilities:[{name:EntryAbility,srcEntry:./ets/entryability/EntryAbility.ets,description:$string:EntryAbility_desc,icon:$media:layered_image,label:$string:EntryAbility_label,startWindowIcon:$media:startIcon,startWindowBackground:$color:start_window_background,exported:true,skills:[{entities:[entity.system.home],actions:[action.system.home]}]}]}}这个配置文件决定了你的HAP里装什么、怎么装。type: entry表示这是主入口模块每个应用有且只能有一个entry模块。deviceTypes决定了这个HAP能装到什么设备上——写错了设备上就装不上。再看构建配置// entry/hvigorfile.tsimport{hapTasks}fromohos/hvigor-ohos-plugin;exportdefault{system:hapTasks,// 使用HAP构建任务插件plugins:[]// 可扩展自定义构建插件}// hvigor/hvigor-config.json5{modelVersion:5.0.0,dependencies:{ohos/hvigor-ohos-plugin:5.0.0// hvigor插件版本}}这是最基础的打包配置基本上DevEco Studio创建工程时就帮你生成好了。但光知道这些不够你得知道怎么改、改了会怎样。进阶用法构建变体与产物定制实际项目中你不可能只有一个构建配置。Debug要调试信息Release要压缩混淆国内版和国际版可能用不同的API地址免费版和付费版功能不同。这就需要构建变体。// entry/oh-package.json5{name:entry,version:1.0.0,description:Please describe the basic information.,main:,author:,license:,dependencies:{},devDependencies:{},dynamicDependencies:{}// 动态依赖配置}// build-profile.json5工程级构建配置{app:{signingConfigs:[],compileSdkVersion:12,compatibleSdkVersion:10,products:[{name:default,signingConfig:default,compatibleSdkVersion:10.0.0,runtimeOS:HarmonyOS,output:{artifactName:MyApp,// 产物名称module:{entry:{compress:{// 压缩配置ark:true,// ArkTS字节码压缩resources:true// 资源压缩}}}}},{name:beta,// Beta构建变体signingConfig:beta,output:{artifactName:MyApp-Beta}}]},modules:[{name:entry,srcPath:./entry,targets:[{name:default,applyToProducts:[default,beta]}]}]}看到products数组没每个对象就是一个构建变体。default是正式版beta是测试版。打包时通过-p product参数指定用哪个变体。再来看一个更实用的场景——条件编译。你想在Debug模式下打印日志Release模式下自动去掉// util/Logger.etsimport{hilog}fromkit.PerformanceAnalysisKit;constDOMAIN0x0001;constTAGMyApp;exportclassLogger{// 通过构建配置注入的宏来控制日志staticdebug(message:string,...args:string[]):void{if(__DEV__){// __DEV__ 是构建时注入的全局变量hilog.debug(DOMAIN,TAG,message,args);}}staticinfo(message:string,...args:string[]):void{hilog.info(DOMAIN,TAG,message,args);}staticerror(message:string,...args:string[]):void{hilog.error(DOMAIN,TAG,message,args);}}完整示例从零到HAP的打包脚本下面是一个完整的打包流程示例包含环境检查、构建、产物验证// scripts/build-hap.etsimport{hapTasks,OhosPluginId}fromohos/hvigor-ohos-plugin;import{HvigorBuildTask,HvigorTask}fromohos/hvigor;// 自定义构建任务打包前检查classPreBuildCheckTaskimplementsHvigorTask{namepreBuildCheck;run():void{console.log([PreBuild] 开始打包前检查...);// 检查module.json5是否存在constmoduleJsonPath./src/main/module.json5;if(!fs.existsSync(moduleJsonPath)){thrownewError(module.json5 不存在请检查模块配置);}// 检查签名配置constbuildProfileJSON.parse(fs.readFileSync(../build-profile.json5,utf-8));constproductsbuildProfile.app?.products||[];if(products.length0){console.warn([PreBuild] 警告未配置构建产物将使用默认配置);}// 检查SDK版本constcompileSdkbuildProfile.app?.compileSdkVersion;if(compileSdkcompileSdk10){thrownewError(compileSdkVersion${compileSdk}过低最低要求 10);}console.log([PreBuild] 检查通过 ✓);}}// 自定义构建任务打包后验证classPostBuildVerifyTaskimplementsHvigorTask{namepostBuildVerify;run():void{console.log([PostBuild] 开始产物验证...);// 查找HAP产物constoutputDir./build/default/outputs/default/;consthapFilesfs.readdirSync(outputDir).filter(ff.endsWith(.hap));if(hapFiles.length0){thrownewError(未找到HAP产物构建可能失败);}// 验证HAP大小for(consthapofhapFiles){conststatfs.statSync(path.join(outputDir,hap));constsizeMBstat.size/(1024*1024);console.log([PostBuild]${hap}:${sizeMB.toFixed(2)}MB);if(sizeMB50){console.warn([PostBuild] 警告${hap}体积超过50MB建议优化);}}console.log([PostBuild] 验证通过 ✓);}}// 注册自定义任务exportdefault{system:hapTasks,plugins:[{pluginId:OhosPluginId.HAP,apply(){// 在assembleHap任务前插入检查this.registerTask(newPreBuildCheckTask(),{before:assembleHap});// 在assembleHap任务后插入验证this.registerTask(newPostBuildVerifyTask(),{after:assembleHap});}}]}命令行打包完整流程# 1. 清理旧产物hvigorw clean# 2. 检查依赖ohpminstall# 3. Debug打包hvigorw assembleHap--modemodule-pmoduleentrydefault-pproductdefault# 4. Release打包带签名hvigorw assembleHap--modemodule-pmoduleentrydefault-pproductdefault-pbuildTyperelease# 5. 查看产物ls-laentry/build/default/outputs/default/踩坑与注意事项坑1module.json5配置错误导致打包失败最常见的错误就是module.json5里写了不存在的Ability或Page。打包工具会校验这些引用找不到文件直接报错。而且报错信息有时候很模糊只告诉你resource not found不告诉你是哪个。解法打包前手动检查一遍引用路径。特别是pages和abilities里的srcEntry确保文件真实存在。如果用了$profile:或$media:引用确保对应的资源文件在resources目录下。坑2SDK版本不匹配compileSdkVersion和compatibleSdkVersion搞混的人太多了。compileSdkVersion是你编译时用的SDK版本compatibleSdkVersion是你的应用最低支持的SDK版本。前者决定你能用哪些API后者决定哪些设备能装你的应用。如果你compileSdkVersion写了12但用的API是10就有的compatibleSdkVersion可以写10——这样SDK 10以上的设备都能装。但如果你compatibleSdkVersion写了12那SDK 10的设备就装不了哪怕你根本没用12的新API。解法compatibleSdkVersion尽量写低覆盖更多设备。compileSdkVersion尽量写高用最新API。坑3HAP体积莫名偏大有时候你代码没几行HAP却好几MB。大概率是resources里塞了大图或者rawfile里有不必要的文件。解法把HAP解压开看看resources/rawfile/和resources/base/media/是重灾区。图片用WebP格式替代PNG/JPGrawfile里不用的文件删掉。坑4hvigorw命令在CI环境执行失败CI服务器上没有DevEco Studiohvigorw执行可能报环境问题。常见的是Node.js版本不对或者ohpm没装。解法CI环境确保Node.js 16ohpm已安装并配置好镜像源。在脚本开头加环境检查# 检查Node版本node--version# 检查ohpmohpm--version# 如果ohpm未安装# export OHPM_HOME/path/to/ohpm# export PATH$OHPM_HOME/bin:$PATH坑5打包产物路径不固定不同版本的DevEco StudioHAP产物路径可能不一样。有的在build/default/outputs/default/有的在build/default/outputs/entry/。解法不要硬编码产物路径用通配符查找# 查找所有HAP文件find.-name*.hap-path*/build/*HarmonyOS 6适配说明HarmonyOS 6对HAP打包做了几项重要调整ABC字节码版本升级HarmonyOS 6使用新版方舟编译器ABC字节码格式有变化。如果你的HAP是用旧版SDK编译的在HarmonyOS 6设备上可能无法运行。确保compileSdkVersion至少为12。module.json5新增字段HarmonyOS 6新增了virtualMachine字段用于指定目标虚拟机类型。默认值是ark一般不需要改。资源索引格式变化HarmonyOS 6的资源索引编译器升级生成的resources.index格式与旧版不兼容。如果你的应用需要同时兼容HarmonyOS 5和6compatibleSdkVersion设为10但compileSdkVersion必须用12编译。hvigor 5.0构建工具升级到hvigor 5.0插件API有Breaking Change。如果你有自定义构建插件需要适配新API。主要变化是HvigorTask接口的方法签名调整run()方法现在返回Promisevoid。HAP签名格式增强HarmonyOS 6的签名算法升级新增SHA-384支持。旧版签名的HAP在HarmonyOS 6上仍可安装但新打包的HAP建议使用新签名算法。总结HAP打包这条链路说复杂也不复杂——源码编译、资源处理、配置校验、打包签名就这几步。说不复杂吧每一步都有坑等着你。关键记住三点配置先行module.json5和build-profile.json5是打包的图纸写错了后面全白搭理解产物HAP就是个ZIP遇到问题解压看看比猜来猜去靠谱善用命令行DevEco Studio能做的hvigorw命令行都能做CI/CD场景必须用命令行