资讯动态

鸿蒙应用包结构拆解:HAP/HAR/HSP与多模块打包避坑指南

发布时间:2026/10/21 9:25:23 来源:尧图企业网站定制
先说一个前几天真实遇到的问题。某开发者把应用从单模块拆成多模块没动任何业务逻辑只是把公共部分抽了几个共享包出来。结果release包一打体积反而比原来大了将近一倍。折腾了一下午最后发现原因特别基础——共享代码被打进了每个HAP重复打包了。这类问题如果不懂鸿蒙的包结构排查起来就是盲人摸象。HarmonyOS鸿蒙应用包结构这件事表面上看就是一堆后缀名HAP、HAR、HSP、APP。但实际开发中你遇到的大多数“包打不出来”“运行闪退”“上架被拒”的问题根源都在这里。这篇文章就把鸿蒙的应用包结构从头到尾拆一遍讲清楚每种包是干什么的、HAP内部长什么样、打包签名链路怎么走以及多模块工程里最常见的包结构坑。1. 为什么说HAP不是APK换个壳三种交付单元的职责边界我在不少技术群里看到刚转鸿蒙的开发者说“HAP不就是APK改个后缀嘛zip解压看里面的东西也差不多。”这个说法大方向没错——HAP确实是压缩包也能解压看到资源文件。但如果只理解到这一层后面做多模块工程的时候一定会吃大亏。1.1 HAP应用的基本交付单元但一个应用通常不止一个HAPHAP的全称是HarmonyOS Ability Package是鸿蒙应用安装和运行的基本单位。一个应用可以由一个或多个HAP组成每个HAP对应工程里的一个模块Module。这里需要纠正一个习惯性认知。很多人以为一个应用就等于一个安装包但在鸿蒙的模型里应用和HAP是“一对多”关系。典型场景是这样的入口HAP负责应用启动入口包含入口Ability和首页页面功能HAP按功能域拆分的模块比如支付模块、直播模块、地图模块各自独立成一个HAP元服务HAP以元服务形式提供的轻量化服务入口按需加载、用完即走。每个HAP有自己的module.json5配置、自己的资源和代码可以独立编译、独立签名。多个HAP合在一起才构成一个完整的APP包交给市场分发。这个关系决定了你在多模块工程里怎么切工程结构也决定了构建产物里会出现几个.hap文件。1.2 HAR和HSP同名“共享包”机制完全是两回事HARHarmonyOS Archive和HSPHarmonyOS Shared Package中文都翻译成“共享包”但工作方式完全不同。HAR是静态共享包。它更像编译期的一堆“原材料”——代码、资源、第三方库在编译阶段被打包进宿主HAP里。引用HAR的模块在构建时会把HAR的内容直接复制进自己的HAP。好处是简单可靠坏处也显而易见如果三个HAP都引用了同一个HAR这个HAR的代码和资源就会存在三份app体积直接膨胀。HSP是动态共享包。它像运行时的一个“公共底座”编译出来独立成包不嵌入宿主HAP。多个HAP可以在运行时共享同一个HSP的代码和资源HSP在应用安装包里只保留一份实体。适合承载真正需要跨模块复用的基础能力比如网络层、数据库封装、工具库、公共UI组件。明白了这两者的区别前面说的“体积翻倍”问题就很好理解——那个人把应该做成HSP的公共模块做成了HARentry和feature各自打了一份进去。1.3 选型判断清单什么情况用HAR什么情况用HSP开发中我基本按这个逻辑决策场景推荐方案理由单模块小应用内部代码简单不拆分直接写在HAP里避免过度设计多模块工程公共代码被2个以上HAP引用HSP只留一份包体小给外部团队发布的纯代码库HARHSP的跨工程共享限制更多需要独立升级的组件HSP可以单独更新只在一个模块内部用到的工具类直接放本地连HAR都不要省得维护一个容易忽略的点HAR如果被多个HAP引用虽然包体膨胀但不会导致功能错误改成HSP之后如果管理不好版本反而会出现运行时接口不一致的问题。所以选型不能只看包体要看团队能不能hold住版本控制。2. 逐一拆解HAP内部目录、配置文件和资源匹配逻辑HAP既然是zip包理论上你可以把它后缀改成.zip直接解压看。我第一次干这事是在排查一个资源不生效的问题解压完看到里面的东西才发现很多“诡异问题”其实结构上就能看出来。2.1 一个典型HAP目录树每个节点是干什么的以Stage模型的entry模块为例构建产物里的HAP解压后大致长这样com.example.demo.hap ├── module.json5 ├── ets/ │ ├── entryability/ │ │ └── EntryAbility.abc │ └── pages/ │ ├── Index.abc │ └── Second.abc ├── resources/ │ ├── base/ │ │ ├── element/ │ │ │ ├── color.json │ │ │ ├── float.json │ │ │ └── string.json │ │ ├── media/ │ │ │ ├── icon.png │ │ │ └── startIcon.png │ │ └── profile/ │ │ ├── main_pages.json │ │ └── module.json5_backup对应资源型配置 │ ├── en_US/ │ │ ├── element/ │ │ │ └── string.json │ │ └── media/ │ └── rawfile/ ├── libs/ │ └── arm64-v8a/ │ └── libnative.so └── resources.indexmodule.json5模块的配置文件所有Ability、权限、设备类型声明都在这里。它决定了一个HAP的“身份”。ets目录ArkTS源码编译后的字节码所在位置。.abc文件Ability Byte Code就是可执行产物你在工程里看到的.ets源码在包里是看不到的。resources目录应用资源按限定词拆分子目录。base是默认资源en_US/zh_CN这种是特定语言资源dark/light是深色模式资源。resources.index资源索引文件运行时资源管理框架靠它快速定位资源。libs目录native库.so按CPU架构分目录。注意HAP目录里没有Android那样的AndroidManifest.xml也没有classes.dex。鸿蒙的配置体系是module.json5 资源索引代码体系是.abc字节码。整个包的结构跟APK差别很大这就是为什么拿APK的思维去理解鸿蒙会卡壳的原因。2.2 module.json5里那些关键字段搞懂一半就能避免大半配置错误module.json5是HAP里最值得逐字段读一遍的文件。我挑几个坑最多的字段展开说。{ module: { name: entry, type: entry, description: $string:module_desc, mainElement: EntryAbility, deviceTypes: [phone, tablet], deliveryWithInstall: true, installationFree: false, pages: $profile:main_pages, abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, description: $string:EntryAbility_desc, icon: $media:icon, label: $string:EntryAbility_label, startWindowIcon: $media:startIcon, startWindowBackground: $color:start_window_background, exported: true, skills: [ { entities: [entity.system.home], actions: [action.system.home] } ] } ] } }type字段必须是entry或feature。entry是应用入口模块feature是功能模块。一个APP包里有且只有一个entry类型的HAP但可以有多个feature类型的HAP。如果你的工程里build的时候提示“entry module not found”或者“multiple entry module”基本就是type配错了。deviceTypes字段声明这个模块支持哪些设备形态。这里最容易出现的问题是入口的skills配了桌面图标但deviceTypes里没包含那个设备类型导致应用在某种设备上看不到图标。我见过一个案例应用在平板上怎么都装不上最后发现它的deviceTypes只有[phone]。mainElement字段指向入口Ability。对entry模块来说它决定应用启动时先拉起哪个Ability。如果这里填的名字和abilities数组里对不上编译可能不报错但运行时会闪退。还有一个在开发工具里不太显眼的字段deliveryWithInstall和installationFree。前者表示HAP是否随应用安装后者表示是否支持免安装元服务场景。对普通应用来说deliveryWithInstall保持true即可改成false会导致HAP不随应用下发。2.3 resources目录的资源匹配逻辑为什么你改了英文不生效resources目录是所有“看似诡异、实则规则明确”问题的重灾区。它的子目录命名有严格格式限定词_限定词...比如en_US、zh_CN、dark、land、en_US_dark。系统运行时会根据设备当前的语言、区域、深浅色、屏幕方向等条件优先去最匹配的限定词目录找资源找不到就往上一级找直到base兜底。例如你的英语用户看到的中文文案大概率是resources/en_US/element/string.json里缺少对应key系统只能回落去base取中文。这个机制本身不复杂但开发中容易踩到两个细节限定词目录的拼写必须符合规范。IDE里右键新建资源目录会引导你选择手写容易把en-US写成en_US才是对的注意是下划线不是中划线。同一资源的key在不同限定词目录中必须一致。base目录里定好了app_nameen_US目录里如果笔误写成了app_namee英文环境下这个key就找不到直接显示key原文。3. 从工程目录到安装包打包签名链路的完整过程理解了HAP内部结构再看构建过程就容易了。一个鸿蒙工程从源码到可安装的APP包要经历依赖解析、模块编译、打包合并、签名校验四步。3.1 一次完整打包经历的四个阶段第一阶段是依赖解析。DevEco Studio同步工程时会读取每个模块的oh-package.json5把声明的HAR、HSP依赖拉下来。这里同步失败或依赖找不到后面编译必然报错。第二阶段是模块编译。ArkTS源码被编译成.abc字节码资源文件生成索引。这一步最耗时也是绝大多数编译报错的产生地——类型错误、语法错误、资源引用错误都在这个阶段暴露。第三阶段是打包合并。每个模块独立打包成HAP或HSP。多模块工程里这个阶段你会在build目录下看到按模块生成的多个产物。第四阶段是签名与组装。所有HAP/HSP经过签名后组装成APP包生成pack.info描述文件。这个文件记录应用包含哪些HAP/HSP、版本信息、包名等元数据相当于整个APP包的“清单”。构建产物路径需要留意模块产物的HAP在各自模块的build目录下最终APP包在工程根目录的build/outputs下。很多人在模块目录里翻半天找不到APP包就是这个原因。3.2 调试签名与发布签名为什么不能混用签名机制是打包链路里“坑密度”最高的环节。鸿蒙的签名体系涉及三样东西密钥库.p12文件、证书文件.cer、Profile文件.p7b。开发调试阶段DevEco Studio会自动生成一个调试证书配置到build-profile.json5里你不需要管太多。但发布上架前这个调试签名就不能用了——应用市场要求使用正式证书签名否则应用市场上传时直接校验失败。签名混用导致的典型报错是Signing certificate mismatch校验不通过会让“本地能装、市场拒收”的诡异局面出现。还有个容易被忽略的点Profile文件绑定了包名bundleName和设备标识UDID。测试机多了之后调试Profile可能把设备加满新设备装不上需要重新生成Profile而不是去改代码。提示发布签名一旦配置到build-profile.json5里就不要随意更换密钥库。如果密钥库丢了或密码忘了证书无法重签只能走证书找回流程非常麻烦。建议把.jks/.p12和密码单独加密备份。3.3 用hdc命令验证包安装后的真实结构打包成功不等于包结构没问题。我习惯装完包之后用hdc命令再确认一遍。hdc install com.example.demo.hap hdc shell bm dump -n com.example.demobm dump输出里可以看到应用包含哪些module、每个module对应HAP的存放路径。如果应然是两个HAP一个HSP这里只会看到两个HAP路径就要回去查HSP是不是没打进去。还有一个验证技巧把HAP直接用压缩软件打开看不完整活动资源会索引错乱但看目录层级、module.json5结构、resources目录分组还是够用的。做资源国际化验证时直接对比不同限定词目录里的文件是否齐全比在设备上切语言反复试要高效得多。4. 多模块工程里最容易踩的两个包结构坑多模块工程是包结构知识的主考场。我见过不算少的工程依赖关系一复杂包就出问题。这里挑两个发生率最高、排查链路也最典型的坑完整走一遍。4.1 典型多模块工程的目录组织方式先看一个常见布局AppScope/ ├── app.json5 entry/ feature_cart/ feature_user/ shared_common/ (HSP类型) shared_network/ (HSP类型) build-profile.json5 oh-package.json5 hvigorfile.tsAppScope/app.json5里配置的是应用全局信息比如bundleName、版本号。各个业务模块是独立的HAPshared开头的模块是HSP。在DevEco Studio中“模块是HAP还是HSP”是在创建模块时决定的但也可以通过模块下的build-profile.json5里调整。容易阴人的是一个工程里只能有一个entry类型模块其他业务模块必须是feature类型。如果你从现有工程复制了一个模块改成业务模块忘记把type从entry改成feature构建时会直接报“multiple entry modules”。4.2 坑一HAR被重复打入多个HAP包体膨胀到怀疑人生回到文章开头那个案例。某开发者把公共工具抽成了common_harHARentry和feature_cart都在引用它。构建后APP包体积飙到原来的1.8倍。排查链路是这样的先看构建日志输出里会列出每个HAP产物的大小。发现entry-default.hap有8MBfeature_cart-default.hap有6MB但两个模块的业务代码加起来不超过5MB直觉告诉我是公共部分重复了。把两个HAP分别解压在各自的ets目录里发现了完全相同的类文件路径——common_har的内容真的被复制了两份。这个现象对HAR来说是正常的是它的工作方式。修复方案是把这个公共工具从HAR改成HSP新建shared_common模块原代码迁移然后让entry和feature_cart的oh-package.json5里依赖shared_common。再打包APP包里HSP只有一份实体体积直接降了一半多。提示HAR不是不能用而是要用对地方。只被一个HAP引用的代码、页面级组件库用HAR反而简单。最怕的是“一个公共库被三四个HAP一起引”——这种必然重复打包。4.3 坑二模块依赖方向写反导致编译时序错乱第二个坑发生在跨模块跳转上。业务上需要在feature_cart里跳转到entry的订单详情页开发者直接在feature_cart的oh-package.json5里加了对entry的依赖。构建时开始没什么问题但改了几次代码后hvigor偶尔报出莫名其妙的任务执行失败错误信息指向没有明确的源文件位置只说是“task A depends on task B which depends on task A”。这是典型的循环依赖。依赖方向在包结构上的含义是被依赖的模块必须先编译完成。feature依赖entry意味着entry要等feature编译完但如果entry内部又有代码引用了feature就成了鸡生蛋问题构建时序自然乱。正确的做法跨模块共享的类型、跳转路由常量、基础页面组件一律下沉到一个共享模块HSP或HAR依赖关系从“业务模块互依”改成“业务模块共同依赖共享层”。这样构建图从网状变成树状时序稳定增量编译也更快。5. 自己动手验证包结构健康度的几个实操办法最后分享几个不依赖IDE图形界面、命令行就能完成的包体检方法。这些东西熟练了排查包相关问题会快很多。5.1 从构建日志读出关键信息构建完成后控制台输出的日志里隐藏着核心数据。重点看三类信息生成了哪些HAP/HSP日志会列出产物路径。数一下HAP数量跟预期对比。少了说明有模块没参与打包多了说明有冗余模块。每个产物的大小DevEco Studio的构建面板或命令行输出会标注产物大小。横向对比各模块体积异常膨胀的模块优先怀疑重复打包。签名信息日志末尾有签名相关的输出检查签名的bundleName是否和AppScope/app.json5里的一致。5.2 常见报错关键词与排查方向报错关键词可能原因检查入口multiple entry modules工程里有多个entry类型模块各模块build-profile.json5Signing certificate mismatch证书bundleName与工程配置不一致build-profile.json5的signingConfigsFailed to find module依赖未同步或oh-package里路径写错oh-package.json5 Sync工程resources not found资源key缺失或限定词目录放错对应resources目录pack info invalidAPP包元数据异常pack.info与app.json5版本信息5.3 体积异常模块的“三件套”检查法发现某个HAP体积异常按顺序做三件事先解压HAP看libs目录确认有没有打进多余的.so再看resources目录有没有把只在调试期用的大图、测试资源带进来最后看ets目录如果有大量重复的公共类路径基本就是HAR重复打入。我在实际项目中靠这三板斧解决过几次“明明没加功能包怎么变大了”的疑惑。多数时候结果都是同一个开发者把一段公共代码以HAR形式塞给了多个业务模块编译期复制了太多份。包结构这东西初学时觉得就是几个名词等到真正做多模块、上架、排查线上问题的时候才体会到它的分量。我个人的习惯是每新建一个模块先问一句它是HAP还是HSP依赖关系画出来是不是树状的公共代码到底放哪里。这三个问题想清楚了包结构层面的大多数坑基本就绕开了。最后再分享一个小技巧改完模块依赖关系后别急着编译先在工程根目录跑一次完整的Sync等到依赖解析日志完全干净了再Build。跳过这一步经常会在编译中途才爆出依赖问题排查起来费的时间反而更多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑