HarmonyOS 6.1 开发者盛宴|《灵犀厨房》实战(二十)扩展:【工程集成】主应用 + 元服务 + HSP 共享库——三模块一体化架构

摘要:第 19 篇我们为《灵犀厨房》装上了通知系统——延时提醒 + WantAgent 回到应用。第 20 篇我们构建了一键推荐原子化服务。但这两个模块目前还各居一隅——主应用的登录页、首页、详情页在一个工程,元服务的推荐页在另一个工程。用户从元服务点击菜谱卡片,主应用需要正确接收 Want 参数并直接打开 RecipeDetailPage——这要求两个模块必须在同一工程、同一 bundleName 下运行。本篇,我们将把 entry(主应用)、atomicservice(元服务)、shared(HSP 共享库)三个模块整合到一个工程中,构建标准的三模块一体化架构。严格遵循 HarmonyOS 6.1.0(API 23)规范,代码基于工程实际文件。


一、引言与系列定位

经过前 19 篇的积累,《灵犀厨房》的主应用已经非常完备。第 20 篇又新增了原子化服务模块。但要实现"元服务点击 → 主应用详情页"的闭环,两个模块必须合并到同一个工程——共享 bundleName、共享 HSP 库、共享签名。

这不仅仅是"把两个文件夹放在一起"。它涉及三个关键决策:

决策 问题 本篇答案
应用类型 bundleTypeapp 还是 atomicService app——因为同时包含主应用和元服务
模块类型 atomicservice 的 module.json5type 用什么? feature——API 23 中 feature 模块通过 installationFree: true 提供元服务能力
代码复用 entry 和 atomicservice 如何共享代码? HSP 共享包(shared)——官方推荐方案,编译产物收敛为单份

设计决策:为什么 bundleType: "app" 而不是 "atomicService"

bundleType: "atomicService" 仅适用于整个应用只有一个独立的元服务入口的场景。当我们同时包含主应用(entry)和元服务(atomicservice)时,应用类型是普通的 app——其中的 feature 模块通过 installationFree: true 来提供原子化能力。这是 HarmonyOS 官方文档明确规定的。


二、核心原理与底层机制深度解读

2.1 三模块架构的编译与运行模型

📱 运行时

应用包 (Bundle)
bundleName: com.annan.lingxikitchen

entry.hap
主应用
installationFree: false

atomicservice.hap
元服务
installationFree: true

shared.hsp
共享代码
只打包一份

🔨 hvigor 编译流程

shared HSP
编译为 .hsp

entry HAP
引用 .hsp

atomicservice HAP
引用 .hsp

核心机制

  • shared 模块编译为 .hsp(Harmony Shared Package),不独立运行,只被消费
  • entryatomicservice 编译为 .hap(Harmony Ability Package),各自独立
  • 三个模块共享同一个 bundleName——系统将它们视作同一个应用的不同模块
  • HSP 在最终包中只存在一份副本,entry 和 atomicservice 共享引用

2.2 原子化服务的两种模块声明方式

在 HarmonyOS 中,原子化服务有两种声明方式,取决于应用的整体形态:

场景 AppScope/app.json5bundleType 模块 module.json5type installationFree
纯元服务(整个应用只有一个入口) atomicService entry true
主应用 + 元服务(复合应用) app feature true

本篇选择bundleType: "app" + 模块 type: "feature" + installationFree: true。因为《灵犀厨房》同时包含完整的主应用和轻量的元服务。

2.3 HSP 共享包 vs 跨模块直接 import

升级为

✅ HSP 共享包

shared
Index.ets 导出

import from 'shared'
路径简洁稳定

单份编译产物

❌ 跨模块直接 import

entry
import ../../atomicservice/...

路径不稳定
编译可能报错

两份代码副本

维度 跨模块直接 import HSP 共享包
路径稳定性 依赖文件系统相对路径 通过 oh-package.json5 声明依赖
编译产物 两份副本(各自打包) 单份共享引用
官方推荐度 非标准用法 ✅ 官方推荐方案

三、架构设计 / 核心逻辑图解

3.1 三模块分层架构

♻️ shared 模块(HSP 共享包)

⚛️ atomicservice 模块(元服务)

📱 entry 模块(主应用)

import from 'shared'

import from 'shared'

import from 'shared'

import from 'shared'

import from 'shared'

🔧 配置层

AppScope/app.json5
bundleType: app

build-profile.json5
modules: [entry, shared, atomicservice]

EntryAbility
路由决策 + RecipeBridge 写入

pages/
LoginPage / Index / RecipeDetailPage / ...

business/
RecipeManager / ShoppingList / ...

services/
NotificationHelper

AtomicserviceAbility
极简初始化

pages/
Index(一键推荐)

business/RecommendEngine
推荐引擎

foundation/model/
Recipe / UserPreference / MockData

utils/RecipeBridge
★ 跨模块参数桥梁

设计原则

  1. HSP 集中共享RecommendEngineRecipe 模型、MockDataRecipeBridge 均位于 shared
  2. UI 轻量化:元服务模块只放 UI 代码(1 个 Ability + 1 个页面),不含业务逻辑
  3. 参数桥梁内置RecipeBridge 在 shared 模块中,EntryAbility 写入,RecipeDetailPage 读取
  4. 独立入口:元服务有自己的 AtomicserviceAbility,不依赖 EntryAbility

3.2 元服务 → 主应用跳转完整时序

📄 RecipeDetailPage 🔗 RecipeBridge 📱 EntryAbility ⚙️ 系统框架 ⚛️ atomicservice 👤 用户 📄 RecipeDetailPage 🔗 RecipeBridge 📱 EntryAbility ⚙️ 系统框架 ⚛️ atomicservice 👤 用户 点击菜谱卡片 startAbility(Want{moduleName:'entry', abilityName:'EntryAbility', params:{recipeId:7,...}}) onCreate(want) 或 onNewWant(want) extractRecipeParams(want) RecipeBridge.set(id, name, ingredients) loadContent('pages/RecipeDetailPage') 加载页面 aboutToAppear() getParams() → undefined RecipeBridge.hasPending() → true {recipeId, recipeName, recipeIngredients} RecipeManager.getRecipeById(7) → 虾仁蒸蛋 显示完整菜谱详情

四、实战:三模块工程搭建

前置说明:本章描述的是项目当前的最终状态,配置与代码均来自工程实际文件。如果你在搭建过程中遇到问题,请参考第 20.5 篇《排错指南》中记录的五个陷阱及修复方案。

Step 1:工程级配置 — build-profile.json5AppScope/app.json5

项目根 build-profile.json5 — 声明三个模块:

{
  "app": {
    "signingConfigs": [],
    "products": [{
      "name": "default",
      "signingConfig": "default",
      "targetSdkVersion": "6.1.1(24)",
      "compatibleSdkVersion": "6.1.0(23)",
      "runtimeOS": "HarmonyOS",
      "buildOption": {
        "strictMode": {
          "caseSensitiveCheck": true,
          "useNormalizedOHMUrl": true
        }
      }
    }]
  },
  "modules": [
    { "name": "entry", "srcPath": "./entry",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }] },
    { "name": "shared", "srcPath": "./shared",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }] },
    { "name": "atomicservice", "srcPath": "./atomicservice",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }] }
  ]
}

核心点解读modules 数组中三个模块的顺序决定了编译顺序——shared 优先编译(HSP 需先产出),entry 和 atomicservice 后编译(依赖 shared)。

AppScope/app.json5 — 应用级配置:

{
  "app": {
    "bundleName": "com.annan.lingxikitchen",
    "vendor": "annan",
    "versionCode": 1000000,
    "versionName": "1.0.0",
    "icon": "$media:layered_image",
    "label": "$string:app_name",
    "bundleType": "app"
  }
}

核心点解读bundleType: "app" 是关键——因为工程同时包含主应用和元服务,不能设为 "atomicService"bundleName: "com.annan.lingxikitchen" 是三个模块的共享标识,元服务通过它调用 startAbility 拉起主应用。


Step 2:shared 模块 — HSP 共享包

shared/oh-package.json5

{
  "name": "shared",
  "version": "1.0.0",
  "description": "共享库",
  "main": "Index.ets",
  "dependencies": {}
}

shared/Index.ets — 统一导出入口:

// entry 和 atomicservice 通过 import from 'shared' 访问此处的导出
export { RecommendEngine, recommendEngine } from './src/main/ets/business/RecommendEngine';
export { Recipe, IngredientItem, StepDetail } from './src/main/ets/foundation/model/Recipe';
export { UserPreference, Gender, ActivityLevel, UserHealthProfile,
  defaultPreference, defaultHealthProfile } from './src/main/ets/foundation/model/UserPreference';
export { allRecipes } from './src/main/ets/foundation/model/MockData';
export { RecipeBridge } from './src/main/ets/utils/RecipeBridge';

shared 内部目录结构

shared/src/main/ets/
├── business/
│   └── RecommendEngine.ets       ← 推荐引擎(多维度加权评分 + 去重)
├── foundation/model/
│   ├── Recipe.ets                ← 菜谱数据模型
│   ├── MockData.ets              ← 10 道全量菜谱模拟数据
│   └── UserPreference.ets        ← 用户偏好 + 健康档案模型
└── utils/
    └── RecipeBridge.ets           ← ★ 跨模块参数桥梁(EntryAbility ↔ RecipeDetailPage)

核心点解读Index.ets 是 HSP 模块的"面子"——它决定了哪些内容对外可见。shared 内部的其他文件不对消费者暴露,实现良好的封装。entry 和 atomicservice 只需 import { recommendEngine } from 'shared' 即可使用推荐能力。


Step 3:entry 模块 — 主应用配置

entry/oh-package.json5 — 声明 shared 依赖:

{
  "name": "entry",
  "version": "1.0.0",
  "description": "主应用",
  "main": "Index.ets",
  "dependencies": {
    "shared": "file:../shared"
  }
}

entry/src/main/module.json5 — 主应用模块声明(关键配置节选):

{
  "module": {
    "name": "entry",
    "type": "entry",
    "mainElement": "EntryAbility",
    "deviceTypes": ["phone", "tablet", "2in1", "wearable"],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [{
      "name": "EntryAbility",
      "srcEntry": "./ets/entryability/EntryAbility.ets",
      "exported": true,
      "skills": [
        {
          "entities": ["entity.system.home"],
          "actions": ["ohos.want.action.home"]
        },
        // ★ 元服务跳转 deep link URI scheme
        {
          "actions": ["ohos.want.action.viewData"],
          "entities": ["entity.system.default"],
          "uris": [{ "scheme": "lingxikitchen", "host": "recipe" }]
        }
      ]
    }]
  }
}

核心点解读

  • type: "entry" — 这是主应用模块,正常安装到桌面
  • installationFree: false — 不支持免安装(必须下载完整应用)
  • 第二个 skillsohos.want.action.viewData + lingxikitchen://recipe URI scheme,为元服务的 startAbility 提供精确路由匹配

Step 4:atomicservice 模块 — 元服务配置

atomicservice/oh-package.json5 — 声明 shared 依赖:

{
  "name": "atomicservice",
  "version": "1.0.0",
  "description": "元服务",
  "main": "Index.ets",
  "dependencies": {
    "shared": "file:../shared"
  }
}

atomicservice/src/main/module.json5 — 元服务模块声明:

{
  "module": {
    "name": "atomicservice",
    "type": "feature",
    "mainElement": "AtomicserviceAbility",
    "deviceTypes": ["phone", "tablet", "2in1", "wearable"],
    "deliveryWithInstall": true,
    "installationFree": true,
    "pages": "$profile:main_pages",
    "abilities": [{
      "name": "AtomicserviceAbility",
      "srcEntry": "./ets/atomicserviceability/AtomicserviceAbility.ets",
      "exported": true,
      "launchType": "singleton",
      "skills": [{
        "actions": ["ohos.want.action.home"],
        "entities": ["entity.system.home"],
        "uris": [{ "scheme": "lingxikitchen", "host": "atomicservice" }]
      }]
    }]
  }
}

核心点解读

  • type: "feature" — 在 API 23 中,与主应用共存的元服务模块应声明为 feature 类型,而非 atomicService
  • installationFree: true — 核心标志,告诉系统此模块可免安装运行
  • launchType: "singleton" — 元服务只有一个推荐页,单例模式避免重复创建
  • urislingxikitchen://atomicservice 为全局搜索提供索引标识

Step 5:atomicservice 页面 — 一键推荐 UI + Want 跳转

// atomicservice/src/main/ets/pages/Index.ets
import { common, Want } from '@kit.AbilityKit';
import { Recipe, recommendEngine, UserPreference } from 'shared';

@Entry
@ComponentV2
struct AtomicRecommendPage {
  @Local recommendations: Recipe[] = [];
  @Local isLoading: boolean = true;

  aboutToAppear(): void {
    this.loadRecommendations();
  }

  private loadRecommendations(): void {
    const defaultPref: UserPreference = {
      favoriteTags: [], allergies: [], maxCalories: 0
    };
    this.recommendations = recommendEngine.getRecommendations(defaultPref, [], 4);
    this.isLoading = false;
  }

  // ★ 核心:点击卡片 → Want 拉起主应用
  private handleRecipeClick(recipe: Recipe): void {
    const ctx = this.getUIContext().getHostContext() as common.UIAbilityContext;
    const want: Want = {
      bundleName: 'com.annan.lingxikitchen',
      moduleName: 'entry',           // ★ 显式指定主应用模块
      abilityName: 'EntryAbility',
      parameters: {
        recipeId: recipe.id,
        recipeName: recipe.name,
        recipeIngredients: recipe.ingredients
      }
    };
    ctx.startAbility(want);
  }
  // ... build() 渲染推荐卡片列表
}

核心点解读

  • moduleName: 'entry' — 必须显式指定,告诉系统要拉起的是主应用模块而非元服务自身
  • 参数设计recipeId 用于查找完整菜谱数据,recipeNamerecipeIngredients 用于快速显示(无需二次查询)
  • HSP 导入import { recommendEngine } from 'shared',路径简洁且不依赖文件系统层级

Step 6:EntryAbility — 主应用路由(含 RecipeBridge 参数桥梁)

这是整个跳转链路的核心——EntryAbility 必须在冷启动、热启动、后台唤起三种场景下都正确路由到 RecipeDetailPage。

// entry/src/main/ets/entryability/EntryAbility.ets
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { RecipeBridge } from 'shared';

export default class EntryAbility extends UIAbility {
  private pendingRecipeId: string = '';
  private pendingRecipeName: string = '';
  private pendingRecipeIngredients: string[] = [];
  private windowStage: window.WindowStage | null = null;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.extractRecipeParams(want);  // 冷/热启动:提取 Want 参数
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.extractRecipeParams(want);  // 后台唤起:提取 Want 参数
    this.loadRecipeDetailPage();     // 直接加载详情页
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    this.windowStage = windowStage;
    // 路由决策:有 recipeId → 直达详情页;否则走登录流程
    if (this.pendingRecipeId.length > 0) {
      this.loadRecipeDetailPage();
    } else {
      windowStage.loadContent('pages/LoginPage', ...);
    }
  }

  private extractRecipeParams(want: Want): void {
    const recipeId = want?.parameters?.recipeId;
    if (recipeId !== undefined && recipeId !== null) {
      this.pendingRecipeId = String(recipeId);
      this.pendingRecipeName = (want?.parameters?.recipeName as string) ?? '';
      this.pendingRecipeIngredients = (want?.parameters?.recipeIngredients as string[]) ?? [];
    }
  }

  // ★ 核心:通过 RecipeBridge 传递参数,再用 loadContent 加载页面
  private loadRecipeDetailPage(): void {
    RecipeBridge.set(this.pendingRecipeId, this.pendingRecipeName, this.pendingRecipeIngredients);
    this.windowStage.loadContent('pages/RecipeDetailPage', callback);
  }
}

核心点解读

  • 双入口参数提取onCreateonNewWant 都调用 extractRecipeParams,覆盖冷启动和后台唤起
  • windowStage.loadContent 而非 router.pushUrlrouter.pushUrl 在 UIAbility 生命周期方法中不可用(无页面上下文),loadContent 是唯一可靠方式
  • RecipeBridge 参数桥梁:不依赖 LocalStorage(API 23 不可用),不使用 router.replaceUrl(会导致二次挂载)。具体原理见第 20.5 篇排错指南
  • 路由决策pendingRecipeId.length > 0 判断是否为元服务跳转,是则跳过登录页直接加载详情

Step 7:RecipeDetailPage — 双通道参数读取

// entry/src/main/ets/pages/RecipeDetailPage.ets
import { RecipeBridge } from 'shared';

aboutToAppear(): void {
  // ★ 优先级: Router 参数(页面内正常跳转)> RecipeBridge(元服务跳转)
  const routerParams = this.getUIContext().getRouter().getParams() as Record<string, Object>;
  const params = routerParams ?? this.getParamsFromBridge();

  if (params) {
    const recipeId = Number(params['recipeId']) || 0;
    const recipeName = (params['recipeName'] as string) ?? '';
    const recipeIngredients = (params['recipeIngredients'] as string[]) ?? [];
    const fullRecipe = recipeManager.getRecipeById(recipeId);
    if (fullRecipe) {
      this.recipe = fullRecipe;
      this.recipe.name = recipeName;
      this.recipe.ingredients = recipeIngredients;
    }
    this.ingredientVM.initFromIngredients(recipeIngredients);
  }
}

private getParamsFromBridge(): Record<string, Object> | undefined {
  if (RecipeBridge.hasPending()) {
    const result: Record<string, Object> = {
      'recipeId': RecipeBridge.recipeId,
      'recipeName': RecipeBridge.recipeName,
      'recipeIngredients': RecipeBridge.recipeIngredients
    };
    RecipeBridge.clear();  // 读取后清除,避免重复跳转
    return result;
  }
  return undefined;
}
通道 触发场景 参数来源
RoutergetParams() 主应用内从首页点击菜谱卡片 router.pushUrl({ url, params })
RecipeBridge 元服务卡片点击 → EntryAbility → loadContent RecipeBridge.set()

五、代码交付清单

文件 新增/修改 职责
build-profile.json5(根目录) 修改 modules 数组注册 entry、shared、atomicservice
AppScope/app.json5 已有 bundleType: "app", bundleName: "com.annan.lingxikitchen"
shared/Index.ets 修改 导出 RecommendEngine、Recipe、UserPreference、MockData、RecipeBridge
shared/src/main/ets/utils/RecipeBridge.ets 新增 类型安全的静态参数桥梁(EntryAbility ↔ RecipeDetailPage)
entry/oh-package.json5 修改 声明 "shared": "file:../shared" 依赖
entry/.../module.json5 修改 新增 ohos.want.action.viewData skill + URI scheme
entry/.../EntryAbility.ets 重写 双入口参数提取 + RouteBridge + 路由决策(三场景覆盖)
entry/.../RecipeDetailPage.ets 修改 aboutToAppear 双通道回退:Router > RecipeBridge
entry/.../RecipeManager.ets 修改 getRecipeById 参数兼容 number | string
atomicservice/oh-package.json5 已有 声明 "shared": "file:../shared" 依赖
atomicservice/.../module.json5 修改 type: "feature", installationFree: true, skills 添加 uris
atomicservice/.../Index.ets 修改 Want 包含 moduleName: 'entry',from ‘shared’ 导入

六、设计决策

决策 选择 理由
应用类型 bundleType: "app" 同时包含主应用和元服务,不适用纯原子化服务类型
原子化服务模块类型 type: "feature" API 23 中 feature 模块通过 installationFree: true 提供元服务能力
代码复用方案 HSP 共享包(shared) 编译产物收敛为单份,路径稳定,官方推荐
参数传递方案 RecipeBridge 静态类 不依赖 LocalStorage(API 23 不可用)、不依赖 router.pushUrl(UIAbility 不可用)
冷启动有 recipeId 直接加载 RecipeDetailPage,跳过登录 元服务是"种草"入口,详情页应免登录查看
onNewWant 导航 windowStage.loadContent router.pushUrl 在 UIAbility 生命周期中不可用
双通道参数读取 Router 优先,RecipeBridge 兜底 兼容主应用内导航和元服务跳转两种场景
元服务 skills 添加 uris 为全局搜索提供索引标识,用户搜索"今天吃什么"可直达服务

七、运行与结果验证

7.1 操作步骤

  1. DevEco Studio 中依次运行三个模块(或直接 Run entry/atomicservice 任一模块,系统会自动部署全部)
  2. 启动元服务(通过 hdc 命令或全局搜索)→ 加载推荐页,显示 4 道菜谱卡片
  3. 点击任意卡片 → 主应用被拉起,直接显示 RecipeDetailPage,包含完整食材清单和制作步骤
  4. 按返回键回到主应用 → 再次点击元服务卡片 → 主应用从后台唤起 → 同样直达详情页

启动元服务的 hdc 命令:

hdc shell aa start -a ohos.want.action.home -b com.annan.lingxikitchen -m atomicservice

7.2 预期日志

收到元服务跳转参数 → recipeId: 7, name: 虾仁蒸蛋
[RecipeBridge] 参数已缓存: id=7, name=虾仁蒸蛋
[RecipeDetail] 路由参数: {"recipeId":"7","recipeName":"虾仁蒸蛋",...}
[RecipeManager] 获取菜谱: id=7, name=虾仁蒸蛋
[IngredientVM] 初始化食材清单(从字符串数组),共 3 项
[RecipeDetail] 菜谱详情加载: 虾仁蒸蛋, 共3步
菜谱详情页加载成功 (元服务跳转)

7.3 路由覆盖矩阵

主应用状态 触发方法 页面 参数通道
未启动(冷启动) onCreateonWindowStageCreateloadRecipeDetailPage RecipeDetailPage RecipeBridge
已销毁(热启动) 同上 同上 同上
后台运行 onNewWantloadRecipeDetailPage RecipeDetailPage RecipeBridge
正常启动 onWindowStageCreateloadContent('pages/LoginPage') LoginPage
主应用内导航 首页 → router.pushUrl RecipeDetailPage Router params

八、注意事项

  • 包名统一:三个模块共享 bundleName: "com.annan.lingxikitchen",模块级 module.json5 中无需重复设置,由 AppScope/app.json5 继承
  • 签名一致:所有模块必须使用相同的调试签名,否则无法互相调用(startAbility 会失败)
  • 资源文件命名:entry 和 atomicservice 中的同名资源(如 string.json 中的 module_desc)需分别定义值,但 string name 可以相同——因为各模块独立读取自己的资源文件
  • HSP 不独立运行:shared 模块不能单独部署,只能被 entry/atomicservice 依赖消费
  • 勿混用 loadContent 和 Router APIloadContent 加载的页面不在 Router 栈中,后续调用 router.replaceUrl 会导致页面二次挂载

九、本阶段总结与下篇预告

今天,我们将《灵犀厨房》的主应用和元服务整合到了一个标准的三模块工程中:

  • 三模块一体化架构entry(主应用)+ atomicservice(元服务)+ shared(HSP 共享包),共享 bundleName,统一签名
  • 配置层精确定义bundleType: "app" + type: "feature" + installationFree: true,严格遵循 API 23 规范
  • 参数传递闭环RecipeBridge 静态桥梁 + windowStage.loadContent + 双通道回退读取,覆盖冷启动/热启动/后台唤起/页面内导航全部场景
  • 代码零重复:推荐引擎、数据模型、参数桥梁全部在 shared 中,entry 和 atomicservice 各司其职

现在,用户从桌面搜索"今天吃什么"直达元服务推荐页,点击菜谱卡片,主应用无缝切换到详情页——从"搜索"到"烹饪"的最短路径已经打通。

但这段旅程并非一帆风顺。在实际联调中,我踩了五个陷阱——LoginPage 拦截、router.pushUrl 报错、router.replaceUrl 二次挂载、LocalStorage 编译失败、打补丁循环。这些问题的根源和修复方案,请参考——

📚 配套排错指南第 20.5 篇《元服务跳转主应用——Want 参数传递的五个陷阱与架构修复》

下篇预告:第 21 篇《【服务卡片】在桌面查看烹饪进度——主进程强推与跨进程桥接》。


📚 本系列持续更新中:下一篇将详细介绍【服务卡片】在桌面查看烹饪进度——主进程强推与跨进程桥接。

🔗 专栏入口:[《HarmonyOS6.1全场景实战》合集]

📦 获取基线版本源码包包括第1-15篇所有代码 + 架构文档 + Flask 后端

如果你觉得这篇文章对你有帮助,请不要吝啬你的点赞 👍、收藏 ⭐ 和评论 💬。你的支持,是我继续输出高质量技术内容的全部动力。
纯血鸿蒙,三模块一体。我们下一篇见!

Logo

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

更多推荐