HarmonyOS NEXT 源码解析与项目复盘:架构、设计模式与工程实践

前言

经过前 28 篇博客的逐步讲解,HarmonyExplorer 项目的各功能模块已完整呈现。本文作为系列倒数第二篇,将从全局视角对项目进行源码级回顾与复盘,深入分析 KitManager 和 ToolManager 的核心实现,梳理数据流转链路,总结设计模式应用,并诚实地复盘开发中遇到的技术难点与解决方案。参考 HarmonyOS 架构设计 了解官方架构理念。

一、项目整体架构回顾

1.1 分层架构总览

HarmonyExplorer 采用 五层分层架构,从上到下职责逐层下沉:

层级 模块 职责 关键技术
UI 层 pages, components 界面展示与交互 ArkUI, @State, @Link
ViewModel 层 viewmodel 状态管理与业务编排 @Observed, @ObjectLink
Repository 层 repository 数据访问与缓存 Preferences, File Kit
Service 层 service 业务逻辑封装 TaskPool, 异步处理
Kit 层 kits, manager HarmonyOS 能力封装 File Kit, Image Kit

1.2 目录结构映射

项目目录与架构层的对应关系清晰明了:

  • pages/ - UI 层,15 个页面
  • components/ - UI 层,22 个公共组件
  • repository/ - Repository 层,数据访问
  • service/ - Service 层,业务逻辑
  • manager/ - KitManager 和 ToolManager
  • kits/ - Kit 层,系统 Kit 封装
  • model/ - 数据模型定义
  • utils/ - 13 个工具类
  • database/ - 数据库与持久化
  • constants/ - 常量与默认值
  • theme/ - 主题资源

清晰的目录结构是大型项目可维护性的基础。每个目录职责单一,文件归属明确,新人可以快速定位代码。

二、KitManager 源码分析

2.1 KitManager 核心设计

KitManager 是 HarmonyOS Kit 能力的统一入口,采用 单例模式 管理所有 Kit 实例。通过懒加载避免启动时初始化所有 Kit。

export class KitManager {
  private static instance: KitManager | null = null;
  private fileKit: FileKit | null = null;
  private imageKit: ImageKit | null = null;
  private mediaKit: MediaKit | null = null;
  private pickerKit: PickerKit | null = null;
  private shareKit: ShareKit | null = null;
  private notificationKit: NotificationKit | null = null;

  private constructor() {}

  static getInstance(): KitManager {
    if (this.instance === null) {
      this.instance = new KitManager();
    }
    return this.instance;
  }

  getFileKit(): FileKit {
    if (this.fileKit === null) {
      this.fileKit = new FileKit();
    }
    return this.fileKit;
  }

  getImageKit(): ImageKit {
    if (this.imageKit === null) {
      this.imageKit = new ImageKit();
    }
    return this.imageKit;
  }

  initAllKits(context: Context): void {
    this.getFileKit().init(context);
    this.getNotificationKit().init(context);
    this.getMediaKit().init(context);
    LogUtil.info('所有 Kit 初始化完成');
  }
}

2.2 Kit 封装模式

每个 Kit 封装类遵循统一的封装模式,对外提供语义化接口,对内调用 HarmonyOS API 并处理异常:

export class FileKit {
  private context: Context | null = null;

  init(context: Context): void {
    this.context = context;
  }

  async readFile(path: string): Promise<string> {
    if (this.context === null) {
      throw new Error('FileKit 未初始化');
    }
    try {
      const content: string = await FileUtil.readFileContent(path);
      return content;
    } catch (error) {
      LogUtil.error('FileKit.readFile 失败: ' + error.message);
      throw error;
    }
  }

  async writeFile(path: string, content: string): Promise<void> {
    if (this.context === null) {
      throw new Error('FileKit 未初始化');
    }
    try {
      await FileUtil.writeFileContent(path, content);
    } catch (error) {
      LogUtil.error('FileKit.writeFile 失败: ' + error.message);
      throw error;
    }
  }
}

三、ToolManager 源码分析

3.1 注册与执行流程

ToolManager 的核心源码在上一篇文章中已详细展示,这里从数据流角度复盘其执行链路:

  1. Toolbox 页面获取工具列表并渲染 ToolCard
  2. 用户点击 ToolCard,触发 onToolClick 回调
  3. ToolManager.executeTool 被调用,查找 ITool 实例
  4. 执行 tool.onActivate 激活工具
  5. 执行 tool.execute(input) 核心逻辑
  6. 记录 ToolHistory 历史记录
  7. 执行 tool.onDeactivate 停用工具
  8. 返回 ToolResult 给 UI 层展示

3.2 设计模式总结

ToolManager 中应用了多种设计模式,提升了系统的可扩展性:

设计模式 应用位置 作用
单例模式 KitManager 全局唯一实例管理
工厂模式 ToolRegistry 统一创建工具实例
策略模式 ITool 接口 不同工具不同执行策略
模板方法 AbstractTool 公共流程固定,子类实现差异
观察者模式 IDataSource 数据变化通知 UI 刷新
适配器模式 Kit 封装 适配 HarmonyOS API

四、数据流分析

4.1 完整数据流转链路

以文件列表加载为例,完整数据流从 UI 触发到 Kit 调用的链路如下:

// UI 层: FileExplorerPage.ets
@Entry
@Component
struct FileExplorerPage {
  @State dataSource: FileListDataSource = new FileListDataSource();

  async aboutToAppear(): Promise<void> {
    const files: Array<FileInfo> = await this.viewModel.loadFiles('/');
    this.dataSource.setData(files);
  }
}

// ViewModel 层: FileExplorerViewModel.ets
export class FileExplorerViewModel {
  async loadFiles(dirPath: string): Promise<Array<FileInfo>> {
    const files: Array<FileInfo> = await FileRepository.getFiles(dirPath);
    const setting: SettingModel = AppStorage.get<SettingModel>('setting');
    return this.sortFiles(files, setting.sortType);
  }

  private sortFiles(files: Array<FileInfo>, sortType: SortType): Array<FileInfo> {
    const sorted: Array<FileInfo> = [...files];
    if (sortType === SortType.NAME_ASC) {
      sorted.sort((a: FileInfo, b: FileInfo) => a.name.localeCompare(b.name));
    } else if (sortType === SortType.TIME_DESC) {
      sorted.sort((a: FileInfo, b: FileInfo) => b.modifyTime - a.modifyTime);
    }
    return sorted;
  }
}

// Repository 层: FileRepository.ets
export class FileRepository {
  static async getFiles(dirPath: string): Promise<Array<FileInfo>> {
    const cached: Array<FileInfo> = FileCache.get(dirPath);
    if (cached.length > 0) {
      return cached;
    }
    const files: Array<FileInfo> = await KitManager.getInstance().getFileKit().listFiles(dirPath);
    FileCache.put(dirPath, files);
    return files;
  }
}

数据流从 UI 层发起,经过 ViewModel 的业务编排、Repository 的缓存策略、最终到达 Kit 层调用系统能力。回程数据沿原路返回并驱动 UI 刷新。

五、核心设计模式应用

5.1 状态管理模式

HarmonyExplorer 的状态管理根据作用域选择不同方案:

  • @State:组件内部状态,如当前选中项
  • @Link/@Prop:父子组件状态传递
  • @Observed/@ObjectLink:可观察对象,精准刷新列表项
  • AppStorage:全局共享状态,如设置信息、用户数据
  • @StorageLink:AppStorage 的双向绑定

5.2 依赖注入实践

通过 EntryAbility 在启动时完成核心模块的初始化和注入:

export default class EntryAbility extends UIAbility {
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    // 初始化 Kit
    KitManager.getInstance().initAllKits(this.context);

    // 初始化工具
    ToolRegistry.initAllTools();

    // 加载设置到全局状态
    await PreferenceUtil.init(this.context);
    const setting: SettingModel = await PreferenceUtil.getSetting();
    AppStorage.setOrCreate<SettingModel>('setting', setting);

    // 初始化主题
    ThemeUtil.applyTheme(setting.theme);
    LogUtil.setLogEnabled(setting.isLogEnabled);
  }
}

六、技术难点复盘

6.1 难点一:文件权限动态申请

HarmonyOS 的文件权限模型与传统 Android 差异较大,部分文件操作不需要运行时权限,而媒体库访问需要。参考 权限管理文档

export class PermissionUtil {
  static async requestFilePermission(): Promise<boolean> {
    const permissions: Array<string> = ['ohos.permission.READ_MEDIA'];
    const tokenID: number = await this.getTokenID();
    const status: AbilityAccessCtrl.GrantStatus =
      await AbilityAccessCtrl.createAtManager().checkAccessToken(tokenID, permissions[0]);
    if (status === AbilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
      return true;
    }
    const result: Array<AbilityAccessCtrl.GrantStatus> =
      await this.requestPermissions(permissions);
    return result[0] === AbilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
  }
}

6.2 难点二:大文件内存优化

加载大图片或大文本时容易触发 OOM。解决方案是分块加载和按需解码:

export class LargeFileReader {
  static async readInChunks(path: string, chunkSize: number, onChunk: (chunk: string) => void): Promise<void> {
    const file: fs.File = fs.openSync(path, fs.OpenMode.READ_ONLY);
    const stat: fs.Stat = fs.statSync(file.fd);
    let offset: number = 0;
    while (offset < stat.size) {
      const readSize: number = Math.min(chunkSize, stat.size - offset);
      const buffer: ArrayBuffer = new ArrayBuffer(readSize);
      fs.readSync(file.fd, buffer, { offset: offset, length: readSize });
      onChunk(new TextDecoder('utf-8').decode(buffer));
      offset += readSize;
    }
    fs.closeSync(file);
  }
}

七、遇到的问题与解决方案

7.1 问题汇总

开发过程中遇到的主要问题及解决方案记录如下:

问题描述 根因 解决方案
列表滚动卡顿 ForEach 全量渲染 改用 LazyForEach
图片内存泄漏 PixelMap 未释放 aboutToDisappear 中 release
主题切换不生效 AppStorage 未驱动 使用 @StorageLink 绑定
Preferences 读取为空 未调用 flush put 后调用 flush
TaskPool 传参报错 不可序列化对象 仅传基本类型参数
深色模式图标不可见 使用硬编码颜色 改用 $r 资源引用

7.2 经验教训

  1. 尽早引入性能分析工具,不要等到问题爆发才优化
  2. ArkTS 严格类型是优势,不要试图绕过类型系统
  3. 资源引用优先于硬编码,确保深色模式和国际化正确
  4. 异步操作必须有错误处理,否则会导致未定义行为

八、代码质量评估

8.1 质量指标

指标 目标值 实际值 评估
代码规范遵守率 100% 98% 优秀
单元测试覆盖率 60% 55% 良好
无 any 类型 100% 100% 优秀
组件复用率 70% 75% 优秀
告警数量 0 2 良好

8.2 代码规范检查

# 使用 DevEco Studio 的 Code Linter 检查
# Tools -> Code Linter -> Run

# 常见规范检查项:
# 1. 禁止使用 any 类型
# 2. 禁止使用 as 类型断言(除 Record 外)
# 3. 必须使用显式类型标注
# 4. 箭头函数代替普通函数
# 5. 命名接口代替匿名类型

九、改进方向

9.1 短期改进

  1. 补充单元测试,提升覆盖率至 70% 以上
  2. 引入自动化 UI 测试框架
  3. 优化错误处理,统一异常上报
  4. 完善日志系统,支持日志分级导出

统一异常上报的改进方向示例:

export class ErrorHandler {
  static handle(error: Error, context: string): void {
    LogUtil.error(context + ': ' + error.message);
    ToastUtil.show('操作失败,请重试');
    this.reportToMonitor(error, context);
  }

  private static reportToMonitor(error: Error, context: string): void {
    // 上报至监控平台
  }
}

9.2 长期演进

  1. 支持云同步功能
  2. 引入 AI 智能文件分类
  3. 支持多设备协同文件管理
  4. 开放 Tool SDK 允许第三方工具接入

9.3 架构演进路线

项目架构不是一成不变的,需要随着业务规模和技术栈演进持续优化。HarmonyExplorer 的架构演进规划分为三个阶段:

阶段 目标 关键改进
近期 补齐工程化短板 单元测试、CI/CD、错误上报
中期 引入云能力 云同步、AI 分类、数据分析
远期 平台化演进 Tool SDK 开放、插件市场、多端协同

架构演进的核心原则是小步快跑、持续重构,避免大爆炸式重写带来的风险。

在这里插入图片描述

图1:HarmonyExplorer 项目五层架构全景图,展示各层模块与数据流向

十、项目复盘总结

10.1 成功经验

HarmonyExplorer 项目在以下方面取得了成功经验:

  1. 分层架构有效控制了代码复杂度,15 个页面 + 22 个组件开发井然有序
  2. 插件化 ToolManager证明了开闭原则的工程价值,工具扩展零侵入
  3. KitManager 统一封装隔离了系统 API 变化风险
  4. ArkTS 严格类型在编译期消除了大量潜在错误

10.2 不足与反思

  1. 单元测试覆盖不足,部分模块依赖手动测试
  2. 错误处理不够统一,各模块各自实现异常捕获
  3. 国际化资源不完整,部分文案仍为硬编码中文
  4. 文档与代码同步不够及时

项目复盘的最大价值不在于记录成功,而在于诚实面对不足并制定改进计划。每一次复盘都是下一次提升的起点。

总结

通过对 HarmonyExplorer 项目的源码解析与全面复盘,我们验证了分层架构、插件化设计、严格类型约束在 HarmonyOS NEXT 企业级开发中的有效性。KitManager 和 ToolManager 的双 Manager 架构实现了系统能力与业务逻辑的清晰分离。数据流的单向流转和状态管理的分层设计保证了应用的可预测性。项目在代码质量和架构设计上达到了较高水平,但在测试覆盖和国际化方面仍有改进空间。更多 HarmonyOS 工程实践请参考 HarmonyOS 开发最佳实践CSDN 技术社区

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

相关资源

Logo

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

更多推荐