鸿蒙应用开发者激励计划 2025 参赛心得与模块化设计最佳实践

作为一名开发者,我有幸参加了"鸿蒙应用开发者激励计划 2025",通过这次参赛经历,不仅深入了解了鸿蒙系统的模块化设计理念,还掌握了许多实用的开发技巧。今天就来分享一下我的参赛心得体会和模块化设计的最佳实践。

关于本文

记录我参加"鸿蒙应用开发者激励计划 2025"的实战心得,重点分享模块化设计在鸿蒙应用开发中的最佳实践

华为开发者联盟-模块化设计文档永远是你的好伙伴,请收藏!_

  • 本文结合我的参赛经历,分享模块化设计的实战经验
  • 包含完整的代码示例和避坑指南
  • 所有内容基于 HarmonyOS 最新 API 版本实践验证

代码测试环境

环境/工具 版本/说明
DevEco Studio 5.0.3.400
HarmonyOS SDK API Version 12
Node.js 18.19.0
测试设备 HarmonyOS 4.0+ 模拟器

1. 参赛心得分享

1.1 激励计划概览

"鸿蒙应用开发者激励计划 2025"是华为面向全球开发者推出的一项重要举措,旨在鼓励开发者基于 HarmonyOS 构建创新应用。计划涵盖多个赛道,包括:

  • 应用创新赛道:基于鸿蒙特性的创新应用开发
  • 生态工具赛道:鸿蒙开发工具链优化
  • 解决方案赛道:行业场景化解决方案

1.2 参赛准备与资源获取

参赛前,我系统学习了华为开发者联盟提供的官方资源:

  1. 开发者联盟官网https://developer.huawei.com/consumer/cn/

    • 提供最新的 SDK、开发工具和文档
    • 设有专门的社区论坛,可与其他开发者交流
  2. 华为开发者学堂

    • 提供从入门到进阶的视频教程
    • 包含大量实战案例分析
  3. DevEco Studio

    • 鸿蒙官方 IDE,集成了丰富的开发工具
    • 支持一键式应用打包和调试

1.3 参赛过程中的收获

通过这次参赛,我深刻体会到:

  • 鸿蒙系统的独特优势:分布式能力、跨设备协同、流畅的用户体验
  • 模块化设计的重要性:提高代码复用率、降低维护成本
  • 社区支持的力量:遇到问题时,社区总能提供及时的帮助

2. 模块化设计最佳实践

在参赛过程中,我深刻认识到模块化设计是构建高质量鸿蒙应用的关键。下面分享一些实用的模块化设计技巧。

2.1 模块化设计的基本原则

模块化设计应遵循以下原则:

  • 高内聚:模块内部功能紧密相关
  • 低耦合:模块之间依赖关系简单
  • 可复用:模块可以在不同场景下重复使用
  • 可测试:模块易于独立测试

2.2 鸿蒙应用的模块化架构

一个典型的鸿蒙应用模块化架构包括:

├── entry/               # 应用入口模块
│   ├── src/main/arkts/  # 主程序代码
│   └── module.json5     # 模块配置
├── feature/             # 功能模块
│   ├── login/           # 登录功能
│   ├── home/            # 首页功能
│   └── profile/         # 个人中心
├── common/              # 公共模块
│   ├── utils/           # 工具函数
│   ├── components/      # 通用组件
│   └── services/        # 公共服务
└── resources/           # 资源文件
    ├── base/            # 基础资源
    └── rawfile/         # 原始文件

2.3 创建模块化应用的步骤

步骤 1:创建项目结构

使用 DevEco Studio 创建一个新的鸿蒙项目,并按照模块化架构规划目录结构。

步骤 2:配置 module.json5

module.json5中配置模块信息:

{
  module: {
    name: "entry",
    type: "entry",
    description: "应用入口模块",
    mainElement: "EntryAbility",
    deviceTypes: ["phone", "tablet"],
    distro: {
      deliveryWithInstall: true,
      moduleName: "entry",
      moduleType: "entry",
    },
    abilities: [
      {
        name: "EntryAbility",
        srcEntry: "./ets/EntryAbility.ts",
        description: "应用入口ability",
        icon: "$media:icon",
        label: "模块化应用",
        startWindowIcon: "$media:icon",
        startWindowBackground: "$color:white",
        exported: true,
      },
    ],
  },
}
步骤 3:创建功能模块

以登录模块为例,创建一个独立的功能模块:

// feature/login/LoginService.ts

export interface LoginParams {
  username: string;
  password: string;
}

export interface UserInfo {
  userId: string;
  username: string;
  avatar: string;
}

export class LoginService {
  // 登录方法
  static async login(params: LoginParams): Promise<UserInfo> {
    // 模拟登录请求
    await new Promise((resolve) => setTimeout(resolve, 1000));

    return {
      userId: "123456",
      username: params.username,
      avatar: "https://example.com/avatar.jpg",
    };
  }

  // 登出方法
  static async logout(): Promise<boolean> {
    // 模拟登出请求
    await new Promise((resolve) => setTimeout(resolve, 500));
    return true;
  }
}
步骤 4:创建通用组件

在 common/components 目录下创建通用组件:

// common/components/ButtonComponent.ets

@Component
export struct ButtonComponent {
  @Prop text: string;
  @Prop type: 'primary' | 'secondary' | 'danger' = 'primary';
  @Event onClick: () => void;

  private getButtonStyle(): Record<string, string | number> {
    switch (this.type) {
      case 'primary':
        return {
          backgroundColor: '#007AFF',
          color: '#FFFFFF'
        };
      case 'secondary':
        return {
          backgroundColor: '#E5E5EA',
          color: '#000000'
        };
      case 'danger':
        return {
          backgroundColor: '#FF3B30',
          color: '#FFFFFF'
        };
      default:
        return {};
    }
  }

  build() {
    Button(this.text)
      .width('100%')
      .height(48)
      .fontSize(16)
      .backgroundColor(this.getButtonStyle().backgroundColor as ResourceColor)
      .fontColor(this.getButtonStyle().color as ResourceColor)
      .borderRadius(8)
      .onClick(() => this.onClick())
  }
}
步骤 5:在入口模块中使用功能模块

在 entry 模块中引用其他模块的功能:

// entry/src/main/arkts/pages/LoginPage.ets

import { LoginService, LoginParams } from '../../../feature/login/LoginService';
import { ButtonComponent } from '../../../common/components/ButtonComponent';

@Entry
@Component
export struct LoginPage {
  @State username: string = '';
  @State password: string = '';
  @State isLoading: boolean = false;

  private async handleLogin() {
    if (!this.username || !this.password) {
      return;
    }

    this.isLoading = true;
    try {
      const params: LoginParams = {
        username: this.username,
        password: this.password
      };

      const userInfo = await LoginService.login(params);
      console.log(`登录成功: ${JSON.stringify(userInfo)}`);
      // 跳转到首页
    } catch (error) {
      console.error(`登录失败: ${JSON.stringify(error)}`);
    } finally {
      this.isLoading = false;
    }
  }

  build() {
    Column({
      space: 20
    }) {
      Text('用户登录')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 100 })

      TextInput({
        placeholder: '请输入用户名',
        text: this.username
      })
        .width('80%')
        .height(48)
        .border({ width: 1, color: '#E5E5EA' })
        .borderRadius(8)
        .padding({ left: 12 })
        .onChange((value: string) => this.username = value)

      TextInput({
        placeholder: '请输入密码',
        text: this.password,
        type: InputType.Password
      })
        .width('80%')
        .height(48)
        .border({ width: 1, color: '#E5E5EA' })
        .borderRadius(8)
        .padding({ left: 12 })
        .onChange((value: string) => this.password = value)

      ButtonComponent({
        text: this.isLoading ? '登录中...' : '登录',
        type: 'primary',
        onClick: () => this.handleLogin()
      })
        .width('80%')
        .margin({ top: 40 })
    }
    .alignItems(HorizontalAlign.Center)
    .backgroundColor('#F8F8F8')
  }
}

2.4 模块化设计的高级主题

2.4.1 共享模块

对于大型软件,不同业务和基础能力由多个团队开发,各团队之间需要代码仓隔离。如果某个或若干个 HAR 工程模块由某个团队负责,又想代码仓隔离,可以在独立工程中开发这些 HAR,并通过公司私有的 OHPM 仓发布和集成编译产物。

图1 多工程合作模式

这部分可以发布到 OHPM 仓的模块,叫做共享模块,可以将公共能力共享给多个应用使用,如公司内部多个应用使用某个公共能力网络库;或者也可以将该公共能力封装成库贡献给社区,给其他应用集成使用,这样的话这个模块也只能是 HAR 模块。

2.4.2 HAR 和 HSP 的选择与替换

在实际开发中,选择使用 HAR 还是 HSP 需要根据具体需求进行权衡:

选择建议

  • 对于需要按需加载的功能模块,应使用 HSP
  • 对于需要跨应用共享的功能模块,应使用 HSP
  • 对于应用内部使用的功能模块,优先考虑 HAR
  • 对于性能敏感的应用启动模块,应使用 HAR

性能对比

图 4:优化前,使用 HSP 包

优化前,使用HSP包

图 5:优化后,使用 HAR 代替 HSP

优化后,使用HAR代替HSP

替换策略

  • 若使用的 HSP 为集成型 HSP,则无法直接替换为 HAR 包,需要根据实际业务情况进行调整。

2.4.3 按需加载模块

随着应用业务的扩展,应用为用户提供的功能不断增加。然而,不是所有功能都是用户频繁使用的。根据用户运营报告的分析,对于月活跃度较低的功能,可以将其设计为按需加载模块。用户首次从应用市场安装时,只会下载不包含按需加载模块的内容。当用户需要使用特定功能时,可以选择下载并安装相应的功能模块。

按需加载模块有以下好处:

  • 减少包体积:用户从应用市场首次下载的应用不包含按需加载模块,用户看到的包体积减少,从而减少了用户下载和安装时间,减少了用户等待时间。
  • 减少系统资源:应用安装之后所占用的空间也变少(节省 ROM 空间),应用启动时加载的特性少了(节省了 RAM 空间)。
  • 架构演进:定义为按需加载的特性明确,模块间耦合关系清晰,有利于应用架构演进。

如果某个特性做成了按需加载模块,该模块可以设计为 Feature 类型的 HAP 或者 HSP,HAP 和 HSP 都可以实现按需加载,区别在于 Feature 类型的 HAP 可以包含 UIAbility 组件。

2.4.3 多 HAP/HSP 引用相同 HAR 包的影响

在应用开发的过程中,可以使用 HSP 或 HAR 的共享包方式将同类的模块进行整合,用于实现多个模块或多个工程间共享 ArkUI 组件、资源等相关代码。

在多 HAP/HSP 引用相同 HAR 包时,由于共享包的动态和静态差异,HAR 包中的单例可能失效,影响应用冷启动性能。

图2 HAP包和HSP包分别引用相同HAR包

如上图所示,工程内包含三个模块:HAP 包作为应用主入口模块,HSP 包作为应用主界面显示模块,HAR_COMMON 集成了所有通用工具类,其中 funcResult 是 func 方法的执行结果。

当 HAP 和 HSP 模块同时引用 HAR_COMMON 模块时,会破坏 HAR 的单例模式。因此,HAP 和 HSP 模块在使用 HAR_COMMON 中的 funcResult 时,会导致 func 方法在两个模块加载时各执行一次,从而增加文件的执行时间。

仅从性能角度考虑,可以采用以下方式进行修改,以缩短冷启动阶段的耗时。

图3 切换为HAP包和HAR包分别引用相同HAR包

说明:

  • 在多 HAP/HSP 引用相同 HAR 包的情况下,如果 HSP 包和 HAR 包均能满足业务需求,建议将 HSP 包改为 HAR 包。
  • 若使用的 HSP 为集成型 HSP,则无法直接替换为 HAR 包,需要根据实际业务情况进行调整。

2.4.4 工程模型选择策略

根据应用需求和架构特点,可以选择不同的工程模型:

非按需加载工程模型

对于不需要按需加载且仅包含一个 Entry 类型 HAP 的应用,可以全部采用 HAR 进行开发设计:

图 6:非按需加载工程模型

非按需加载工程模型

说明:这里的"仅有一个 HAP"是指一种设备类型仅包含一个 HAP,而不是指.app 文件包中仅有一个 HAP。.app 文件包可以包含其他设备的 HAP 包,例如手表和大屏设备的 HAP 包,以支持多设备分发。

设计成 HAR 包的优点:

  1. 全部编译进 HAP,无额外的 HSP,节省 HSP 的安装和加载成本
  2. HAR 在编译进 HAP 时,可以利用 ArkTS 的语言特性和编译器功能,做类型推断和编译优化
  3. 代码工程架构简单,后续演进较为灵活
包含按需加载模块的工程模型

在单 HAP 工程中实现按需加载功能时,对应的组件需采用 HSP 作为按需加载模块。需要根据业务需求在 App Size 与启动性能之间做好平衡:

图 7:公共依赖模块通过 HSP 模块壳承载

公共依赖模块通过HSP模块壳承载

App Size 优先策略
将公共依赖的模块封装在一个 HSP 模块壳中,减少重复存储,节省应用整体大小。

性能优先策略
优先考虑应用启动性能,合理控制 HSP 数量,避免过多 HSP 影响安装效率和 App 启动性能。

图 8:公共依赖模块使用 HAR 模块承载

公共依赖模块使用HAR模块承载

2.4.5 多 HAP 工程设计策略

对于同一个设备类型,如果需要实现不同的独立功能模块,并且相对独立,以及具有单独的入口的功能特性,建议做成一个独立特性的 HAP,按需下载安装。此时一个 App 包中,就会有多个 HAP 包,其中有且仅有一个 Entry 类型的 HAP,其他的均是 Feature 类型的 HAP。

包含公共能力模块的多 HAP 工程

性能优先策略

一般多 HAP 应用架构普适性采用以下模型,除了产品组件中存在 HAP 包之外,其余的均是 HAR 包:

图 9:多 HAP 工程模块示意图(性能优先)

多HAP工程模块示意图(性能优先)

App Size 优先策略

将公共的 HAR 包封装到 HSP 工程中,确保每个 HAR 包在 App 编译产物中仅出现一次,从而减小 App Size:

图 10:多 HAP 工程模块示意图(App Size 优先)

多HAP工程模块示意图(App Size优先)

说明:需要注意,在应用间共享的 HAR 包,原则上是不允许依赖 HSP 包,因为 HSP 包是专属于应用,和 bundleName 进行了绑定,一旦 HAR 包依赖于应用内 HSP,该 HAR 包就丢失了共享性,无法再给其他应用共享。

3. 模块化设计的避坑指南

在实施模块化设计时,需要注意以下几点:

3.1 避免过度模块化

西兰花警告 ⚠️:不要为了模块化而模块化,过度模块化会增加项目的复杂性。只有当功能足够独立且可能被复用时,才考虑将其拆分为独立模块。

3.2 合理管理模块依赖

  • 避免循环依赖:A 依赖 B,B 又依赖 A
  • 控制依赖深度:尽量保持依赖链不超过 3 层
  • 使用接口隔离:通过接口定义模块间的交互,减少具体实现的依赖

3.3 统一资源管理

  • 使用统一的资源命名规范
  • 将公共资源放在 resources 目录下
  • 避免在模块内部硬编码资源路径

3.4 模块测试策略

  • 为每个模块编写独立的单元测试
  • 使用 DevEco Studio 的测试工具进行自动化测试
  • 定期执行集成测试,确保模块间协作正常

4. 参赛作品展示

在本次激励计划中,我开发了一款名为"健康饮食助手"的鸿蒙应用,该应用采用了模块化设计,主要功能包括:

  • 饮食记录:记录每日饮食情况
  • 营养分析:分析饮食的营养成分
  • 健康建议:根据用户情况提供个性化建议
  • 社交分享:分享健康食谱和饮食心得

应用的模块化架构设计使得功能扩展和维护变得非常方便。

5. 总结与展望

通过参加"鸿蒙应用开发者激励计划 2025",我不仅获得了宝贵的开发经验,还深刻认识到模块化设计在鸿蒙应用开发中的重要性。

未来,我将继续深入学习鸿蒙系统的新特性,不断优化模块化设计能力,为构建更加高效、稳定的鸿蒙应用贡献自己的力量。

参考文档


我是盐焗西兰花,一名热爱技术,热爱分享的开发者,希望我的参赛心得和模块化设计最佳实践能对大家有所帮助。如果你有任何问题或想法,欢迎在评论区交流!我们下期见!🥦


需要参加鸿蒙认证的请点击 鸿蒙认证链接

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐