OpenHarmony HSP 动态分包完整实战:分包加载、传参、卸载避坑全流程

OpenHarmony HSP 动态分包完整实战:分包加载、传参、卸载避坑全流程 前言在大型鸿蒙项目、毕设项目中如果所有页面全部打包进 entry 主模块会出现安装包体积大、冷启动缓慢、闲置页面持续占用内存等问题。HSPHarmony Shared Package动态分包是官方提供的按需加载方案重型编辑器、相册、视频页面可独立拆分为分包仅用户点击时加载退出页面立即卸载释放内存。 本文基于前文四层脚手架完整演示 HSP 创建、依赖配置、页面跳转传参、分包卸载、常见报错解决方案所有代码可直接复制运行。一、HSP 与 HAR 核心区别表格类型HAR 静态包HSP 动态分包加载时机编译期合并入主包应用启动全部加载运行时按需加载未访问不占用内存页面支持不能存放 pages 路由页面支持独立 pages 页面可单独跳转依赖规则可被所有模块静态引入不能依赖 HSP仅能依赖 HAR不能依赖其他 HSP复用范围全工程跨模块复用仅当前应用内部使用无法跨项目共享适用场景通用工具、业务实体、基础组件低频、体积大的独立业务页面二、新建 HSP 模块与 module.json5 标准配置1. 创建 hsp_note_editor 模块DevEco Studio 右键项目 → New → Module → Harmony Shared Package模块名hsp_note_editor。2. module.json5 完整配置json{ module: { name: hsp_note_editor, type: hsp, description: 笔记编辑器动态分包, deviceTypes: [phone], deliveryWithInstall: true, pages: [ src/main/ets/pages/EditorPage ] }, dependencies: [ { name: har_base, version: 1.0.0, scope: shared }, { name: har_note, version: 1.0.0, scope: shared } ] }关键配置说明deliveryWithInstall: true打包时分包独立拆分安装主 HAP 时同步安装分包pages 数组注册分包内页面否则路由跳转提示页面不存在仅依赖底层公共 HAR 与对应业务 HAR禁止引入其他 HSP。3. entry 模块依赖配置entry 的 module.json5 dependencies 中添加该分包否则编译找不到模块json{ name: hsp_note_editor, version: 1.0.0, scope: shared }三、HSP 跳转路由工具完整实现承接上文 RouterUtil在har_base/utils/router_util.ets补充 HSP 跳转方法内置登录拦截、异常捕获ets// HSP页面跳转 pushHsp(hspName: string, pagePath: string, params?: Object) { // 登录拦截未登录禁止进入分包页面 if (!this.checkLogin()) return; try { router.pushNamedRoute({ bundleName: this.ctx?.bundleName, moduleName: hspName, pagePath: pagePath, params }) } catch (e) { LogUtil.error(RouterUtil, HSP页面跳转失败, e); DialogUtil.alert({ content: 编辑器加载失败请重试 }); } } // 卸载HSP分包释放内存 async unloadHsp(hspName: string) { try { await router.unloadNamedRoute(hspName); LogUtil.info(RouterUtil, 分包${hspName}卸载完成); } catch (e) { LogUtil.warn(RouterUtil, 分包卸载异常, e); } }四、HSP 编辑器页面完整代码hsp_note_editor/pages/EditorPage.etsetsimport ThemeUtil from ohos:har_base/utils/theme import DialogUtil from ohos:har_base/utils/dialog_util import RouterUtil from ohos:har_base/utils/router_util import RdbUtil from ohos:har_base/utils/rdb_util import LogUtil from ohos:har_base/utils/log_util Entry Component struct EditorPage { State title: string State content: string // 接收列表页传递的笔记id新增为0 State noteId: number 0 aboutToAppear() { // 获取路由传递参数 const params router.getParams() as { id?: number } if (params.id params.id 0) { this.noteId params.id this.loadEditNote() } } // 编辑模式回填原有笔记数据 async loadEditNote() { const list await RdbUtil.queryNoteList(0, 100); const target list.find(item item.id this.noteId); if (target) { this.title target.title this.content target.content } } // 保存笔记 async saveNote() { if (!this.title.trim()) { DialogUtil.alert({ content: 标题不能为空 }) return } try { if (this.noteId 0) { // 更新已有笔记 await RdbUtil.updateNote(this.noteId, this.title, this.content) } else { // 新增笔记 await RdbUtil.insertNote(this.title, this.content) } DialogUtil.alert({ content: 保存成功, onConfirm: async () { // 返回列表页卸载当前分包释放内存 RouterUtil.back() await RouterUtil.unloadHsp(hsp_note_editor) } }) } catch (err) { LogUtil.error(EditorPage, 保存笔记失败, err) DialogUtil.alert({ content: 保存失败请重试 }) } } // 页面销毁强制卸载分包防止内存残留 async aboutToDisappear() { DialogUtil.closeAllDialog() await RouterUtil.unloadHsp(hsp_note_editor) } build() { const color ThemeUtil.getColor() const size ThemeUtil.getSize() Column({ space: size.gapLg }) .width(100%) .height(100%) .padding(size.gapMd) .backgroundColor(color.pageBg) { Text(this.noteId 0 ? 编辑笔记 : 新建笔记) .fontSize(size.fontTitle) .fontColor(color.textPrimary) TextInput({ text: this.title, placeholder: 请输入笔记标题 }) .width(100%) .height(size.btnNormal) .fontSize(size.fontMain) .backgroundColor(color.cardBg) .borderRadius(size.radiusSm) .onChange((val: string) this.title val) TextArea({ text: this.content, placeholder: 请输入笔记内容 }) .width(100%) .layoutWeight(1) .fontSize(size.fontAux) .backgroundColor(color.cardBg) .borderRadius(size.radiusSm) .onChange((val: string) this.content val) Button(保存笔记) .width(100%) .height(size.btnNormal) .backgroundColor(color.primary) .borderRadius(size.radiusSm) .onClick(() this.saveNote()) } } }五、entry 首页跳转 HSP 调用示例etsimport RouterUtil from ohos:har_base/utils/router_util import ThemeUtil from ohos:har_base/utils/theme Entry Component struct IndexPage { build() { const color ThemeUtil.getColor() const size ThemeUtil.getSize() Column({ space: size.gapLg }) .width(100%) .height(100%) .padding(size.gapMd) .backgroundColor(color.pageBg) { Text(笔记管理首页) .fontSize(size.fontTitle) .fontColor(color.textPrimary) Button(新建笔记) .width(100%) .height(size.btnNormal) .backgroundColor(color.primary) .borderRadius(size.radiusSm) .onClick(() { // 跳转HSP传参id0代表新建 RouterUtil.pushHsp(hsp_note_editor, src/main/ets/pages/EditorPage, { id: 0 }) }) Button(查看笔记列表) .width(100%) .height(size.btnNormal) .backgroundColor(color.success) .borderRadius(size.radiusSm) .onClick(() RouterUtil.push(pages/note/NoteListPage)) } } }六、HSP 核心规范与内存优化要点分包卸载强制规范页面aboutToDisappear生命周期必须调用unloadNamedRoute卸载分包否则分包代码、图片资源常驻内存多次进出内存持续上涨。资源隔离规范HSP 内部图片、矢量图标添加分包专属前缀editor_避免和 entry、HAR 资源重名打包覆盖。禁止跨 HSP 引用hsp_a 不能导入 hsp_b 的任何 ets 代码跨分包交互只能通过路由传参不能静态 import。数据存储规范分包不能独立封装数据库逻辑所有 RDB 操作统一调用业务 HAR 提供的工具分包仅做页面渲染。全局状态规范分包可正常使用 GlobalStore 全局状态、ThemeUtil 主题工具底层 HAR 全局能力完全共享。七、HSP 高频报错与解决方案报错 1pushNamedRoute 提示页面不存在原因 1HSP 模块 module.json5 中 pages 数组未注册页面路径 原因 2跳转 pagePath 路径拼写错误大小写不匹配 解决核对 pages 配置复制完整页面文件路径填入路由参数。报错 2编译提示模块找不到原因entry 模块 dependencies 未添加 HSP 依赖 解决entry 的 module.json5 添加对应 HSP 依赖同步清理项目缓存重新编译。报错 3多次打开分包内存持续升高原因页面退出未调用 unloadHsp 卸载分包、图片 PixelMap 未释放、全局监听未解绑 解决aboutToDisappear 统一执行卸载分包、关闭弹窗、释放图像资源。报错 4HSP 无法跳转其他 HSP 页面原因官方限制 HSP 之间不能互相路由跳转 解决返回 entry 主页面由 entry 统一调度跳转不同分包。报错 5分包内深色模式切换无响应原因分包内部缓存主题色值未实时调用 ThemeUtil.getColor () 解决build 方法内直接动态获取主题不使用 State 缓存颜色变量。八、文末总结HSP 动态分包是鸿蒙项目轻量化、性能优化的核心手段将低频重型页面独立拆分大幅缩减主 HAP 体积、降低冷启动耗时。结合前文四层架构业务页面拆分至 HSP、基础能力下沉 HAR实现代码复用与按需加载双重收益。 本文完整覆盖 HSP 创建、依赖配置、路由跳转、参数传递、分包卸载全流程配套可直接运行的编辑器页面代码解决分包开发 90% 常见报错适合毕设、商用项目直接落地。 下一篇将讲解 OpenHarmony 图片压缩、相册权限完整工具 ImagePickerUtil 实战。