【HarmonyOS 6.1 全场景实战】《灵犀厨房》实战(排错指南):【服务卡片跳转】页面栈“迷航”——从“回不去的主页”到精准 Tab 唤醒的全链路修复

HarmonyOS 6.1 全场景实战 |《灵犀厨房》实战(排错指南):【服务卡片跳转】页面栈"迷航"------从"回不去的主页"到精准 Tab 唤醒的全链路修复

摘要 :上一篇我们为《灵犀厨房》装上了服务卡片------抬手就能看到烤箱倒计时。但卡片点击后,一个幽灵般的 Bug 悄然出现:点击服务卡片进入厨电页,再点桌面图标,App 直接打开了厨电页,返回键一按就退出,再也回不到首页。经过排查,这不是代码写错了,而是页面栈被"截断"了 。本文将复盘从现象定位、根因分析、方案设计到代码修复的全过程:服务卡片通过 postCardAction 跳转,在 EntryAbility 中原本被直接加载为独立的 KitchenDevicePage,导致 MainContainer 从未进入页面栈。修复后,卡片跳转统一指向 MainContainer,通过 startTab 参数指定厨电 Tab,配合 EntryBridge 静态变量和 emitter 事件,完美实现冷启动、热启动、后台唤起全场景覆盖。本文还记录了修复过程中踩过的 emitter.emit 参数类型陷阱。


一、引言:一个"回不去"的幽灵 Bug

第二十一篇中,我们为《灵犀厨房》实现了桌面烹饪进度卡片。点击卡片,跳转到厨电控制页------看起来一切正常。直到测试时发现一个诡异的现象:

操作步骤 预期行为 实际行为
点击服务卡片 进入厨电控制页 ✅ 正常
按返回键 回到首页 ❌ 直接退出 App
再次点击桌面图标 回到首页或上次停留的页面 ❌ 又打开了厨电页
在厨电页点击返回 回到首页 ❌ 又退出 App

用户被困在了一个"厨电页孤岛"上------无论怎么操作,都无法回到首页、健康页或个人中心。这是一个典型的页面栈被截断问题。


二、核心原理:HarmonyOS 的页面栈机制

要理解这个 Bug,必须先理解 HarmonyOS 的页面导航模型。
#mermaid-svg-6MaTjfjNYobDp2Wb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6MaTjfjNYobDp2Wb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6MaTjfjNYobDp2Wb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6MaTjfjNYobDp2Wb .error-icon{fill:#552222;}#mermaid-svg-6MaTjfjNYobDp2Wb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6MaTjfjNYobDp2Wb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6MaTjfjNYobDp2Wb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6MaTjfjNYobDp2Wb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6MaTjfjNYobDp2Wb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6MaTjfjNYobDp2Wb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6MaTjfjNYobDp2Wb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6MaTjfjNYobDp2Wb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6MaTjfjNYobDp2Wb .marker.cross{stroke:#333333;}#mermaid-svg-6MaTjfjNYobDp2Wb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6MaTjfjNYobDp2Wb p{margin:0;}#mermaid-svg-6MaTjfjNYobDp2Wb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6MaTjfjNYobDp2Wb .cluster-label text{fill:#333;}#mermaid-svg-6MaTjfjNYobDp2Wb .cluster-label span{color:#333;}#mermaid-svg-6MaTjfjNYobDp2Wb .cluster-label span p{background-color:transparent;}#mermaid-svg-6MaTjfjNYobDp2Wb .label text,#mermaid-svg-6MaTjfjNYobDp2Wb span{fill:#333;color:#333;}#mermaid-svg-6MaTjfjNYobDp2Wb .node rect,#mermaid-svg-6MaTjfjNYobDp2Wb .node circle,#mermaid-svg-6MaTjfjNYobDp2Wb .node ellipse,#mermaid-svg-6MaTjfjNYobDp2Wb .node polygon,#mermaid-svg-6MaTjfjNYobDp2Wb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6MaTjfjNYobDp2Wb .rough-node .label text,#mermaid-svg-6MaTjfjNYobDp2Wb .node .label text,#mermaid-svg-6MaTjfjNYobDp2Wb .image-shape .label,#mermaid-svg-6MaTjfjNYobDp2Wb .icon-shape .label{text-anchor:middle;}#mermaid-svg-6MaTjfjNYobDp2Wb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6MaTjfjNYobDp2Wb .rough-node .label,#mermaid-svg-6MaTjfjNYobDp2Wb .node .label,#mermaid-svg-6MaTjfjNYobDp2Wb .image-shape .label,#mermaid-svg-6MaTjfjNYobDp2Wb .icon-shape .label{text-align:center;}#mermaid-svg-6MaTjfjNYobDp2Wb .node.clickable{cursor:pointer;}#mermaid-svg-6MaTjfjNYobDp2Wb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6MaTjfjNYobDp2Wb .arrowheadPath{fill:#333333;}#mermaid-svg-6MaTjfjNYobDp2Wb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6MaTjfjNYobDp2Wb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6MaTjfjNYobDp2Wb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6MaTjfjNYobDp2Wb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6MaTjfjNYobDp2Wb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6MaTjfjNYobDp2Wb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6MaTjfjNYobDp2Wb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6MaTjfjNYobDp2Wb .cluster text{fill:#333;}#mermaid-svg-6MaTjfjNYobDp2Wb .cluster span{color:#333;}#mermaid-svg-6MaTjfjNYobDp2Wb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6MaTjfjNYobDp2Wb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6MaTjfjNYobDp2Wb rect.text{fill:none;stroke-width:0;}#mermaid-svg-6MaTjfjNYobDp2Wb .icon-shape,#mermaid-svg-6MaTjfjNYobDp2Wb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6MaTjfjNYobDp2Wb .icon-shape p,#mermaid-svg-6MaTjfjNYobDp2Wb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6MaTjfjNYobDp2Wb .icon-shape .label rect,#mermaid-svg-6MaTjfjNYobDp2Wb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6MaTjfjNYobDp2Wb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6MaTjfjNYobDp2Wb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6MaTjfjNYobDp2Wb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ❌ 服务卡片截断的页面栈
不存在
MainContainer

(从未进入栈)
KitchenDevicePage

(loadContent 直接加载)
✅ 正常页面栈
MainContainer

(Tab 0: 首页)
RecipeDetailPage

(pushUrl)
KitchenDevicePage

(pushUrl)

图一解读 :正常流程下,用户从首页点击菜谱卡片 → router.pushUrl 打开详情页 → 再点"开始烹饪" → router.pushUrl 打开厨电页。页面栈中依次是 MainContainer → RecipeDetailPage → KitchenDevicePage,按返回键时,系统从栈顶逐个弹出,最终回到 MainContainer。

但服务卡片跳转时,EntryAbility 通过 windowStage.loadContent 直接加载了 KitchenDevicePage,MainContainer 从未进入页面栈。于是按返回键时,系统发现栈中只有一个页面,直接退出 App。而系统"记住"了这个状态,再次点图标时又恢复了这个孤零零的厨电页。


三、架构修正:统一跳转入口

修正的核心思想是:服务卡片不直接加载任何独立页面,而是跳转到 MainContainer,通过 startTab 参数指定要激活的 Tab。
AppIcon MainContainer emitter EntryBridge EntryAbility 服务卡片 AppIcon MainContainer emitter EntryBridge EntryAbility 服务卡片 #mermaid-svg-Fr6Z5DjMTpyB2r1w{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Fr6Z5DjMTpyB2r1w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .error-icon{fill:#552222;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .marker.cross{stroke:#333333;}#mermaid-svg-Fr6Z5DjMTpyB2r1w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Fr6Z5DjMTpyB2r1w p{margin:0;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fr6Z5DjMTpyB2r1w text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Fr6Z5DjMTpyB2r1w .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Fr6Z5DjMTpyB2r1w #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .sequenceNumber{fill:white;}#mermaid-svg-Fr6Z5DjMTpyB2r1w #sequencenumber{fill:#333;}#mermaid-svg-Fr6Z5DjMTpyB2r1w #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .messageText{fill:#333;stroke:none;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .labelText,#mermaid-svg-Fr6Z5DjMTpyB2r1w .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .loopText,#mermaid-svg-Fr6Z5DjMTpyB2r1w .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Fr6Z5DjMTpyB2r1w .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .noteText,#mermaid-svg-Fr6Z5DjMTpyB2r1w .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .actorPopupMenu{position:absolute;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Fr6Z5DjMTpyB2r1w .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fr6Z5DjMTpyB2r1w .actor-man circle,#mermaid-svg-Fr6Z5DjMTpyB2r1w line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Fr6Z5DjMTpyB2r1w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 场景1:冷启动(App 未运行) 场景2:热启动(App 在后台) 场景3:从桌面图标点击(无 startTab) onCreate(want) extractParams: startTab=2 EntryBridge.initialTab = 2 loadContent('pages/MainContainer') aboutToAppear() 读取 EntryBridge.initialTab = 2 currentIndex = 2, changeIndex(2) 显示厨电 Tab onNewWant(want) extractParams: startTab=2 emit({ eventId: 10003 }, { data: { tabIndex: 2 } }) tabChangeCallback 触发 currentIndex = 2, changeIndex(2) 切换到厨电 Tab(无重复加载) onNewWant(无 startTab 参数) startTab 为 undefined,跳过 emitter 通知 保持当前 Tab,不做切换

图二解读 :修正后的架构覆盖了三种场景。冷启动时,通过静态变量 EntryBridge.initialTab 传递 Tab 索引;热启动时,通过 emitter 事件通知已存在的 MainContainer 切换 Tab;从桌面图标正常进入时,不携带 startTab 参数,保持用户上次离开时的页面状态。三条路径统一指向 MainContainer,页面栈完整,返回键行为恢复正常。


四、代码修正清单

4.1 WidgetCard.ets --- 修正跳转目标

typescript 复制代码
// 修正前:直接跳转到独立 KitchenDevicePage
.onClick(() => {
  postCardAction(this, {
    action: 'router',
    abilityName: 'EntryAbility',
    params: { targetPage: 'KitchenDevicePage' }
  });
})

// 修正后:跳转到 MainContainer,携带 startTab 参数
.onClick(() => {
  postCardAction(this, {
    action: 'router',
    abilityName: 'EntryAbility',
    params: {
      targetPage: 'MainContainer',
      startTab: 2  // 指定激活"厨电"Tab
    }
  });
})

4.2 EntryAbility.ets --- 核心路由改造

新增 EntryBridge 静态桥梁(用于冷启动):

typescript 复制代码
export class EntryBridge {
  static initialTab: number = -1;
}

extractParams 中提取 startTab:

typescript 复制代码
private extractParams(want: Want): void {
  const targetPage = want?.parameters?.targetPage as string;
  if (targetPage && targetPage.length > 0) {
    this.pendingTargetPage = targetPage;
    // 提取 Tab 索引并写入静态桥梁
    const startTab = want?.parameters?.startTab as number;
    if (startTab !== undefined && startTab >= 0) {
      EntryBridge.initialTab = startTab;
    }
    return;
  }
  // ... 原有的 recipeId 提取逻辑
}

onNewWant 中无条件处理 startTab(用于热启动):

typescript 复制代码
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  this.extractParams(want);
  if (cookingProgressManager.isActive) return;

  // ★ 无条件处理 startTab 参数
  const startTab = want?.parameters?.startTab as number;
  if (startTab !== undefined && startTab >= 0) {
    EntryBridge.initialTab = startTab;
    emitter.emit(
      { eventId: TAB_CHANGE_EVENT_ID },
      { data: { tabIndex: startTab } }
    );
    return;
  }
  // 原有的菜谱详情页跳转逻辑
  if (this.pendingRecipeId.length > 0) {
    this.loadRecipeDetailPage();
  }
}

4.3 MainContainer.ets --- 响应初始 Tab 与动态切换

冷启动读取静态桥梁:

typescript 复制代码
aboutToAppear(): void {
  if (EntryBridge.initialTab >= 0) {
    const tabIndex = EntryBridge.initialTab;
    EntryBridge.initialTab = -1;
    this.currentIndex = tabIndex;
    this.tabsController.changeIndex(tabIndex);
  }
  // 监听热启动 Tab 切换事件
  emitter.on({ eventId: TAB_CHANGE_EVENT_ID }, this.tabChangeCallback);
}

热启动监听回调:

typescript 复制代码
private tabChangeCallback: Callback<emitter.EventData> = (eventData: emitter.EventData) => {
  const tabIndex = eventData?.data?.tabIndex as number;
  if (tabIndex !== undefined && tabIndex >= 0) {
    this.currentIndex = tabIndex;
    this.tabsController.changeIndex(tabIndex);
  }
};

五、踩坑实录:emitter.emit 参数类型陷阱

修复过程中,emitter.emit 的参数类型报错是一个典型的 API 23 陷阱:

错误写法:

typescript 复制代码
emitter.emit({ eventId: TAB_CHANGE_EVENT_ID, data: { tabIndex: 2 } });
// 报错:'data' does not exist in type 'InnerEvent'

正确写法:

typescript 复制代码
emitter.emit(
  { eventId: TAB_CHANGE_EVENT_ID },   // 第1参数:InnerEvent 对象
  { data: { tabIndex: 2 } }            // 第2参数:EventData 对象
);

emitter 的三个方法签名在 API 23 中极不一致:

方法 第1参数 第2参数
emitter.on { eventId: number } 对象 回调函数
emitter.emit { eventId: number } 对象 { data: { ... } } 对象
emitter.off number 数字 回调函数

核心记忆口诀 :on 传对象,emit 拆两个(对象 + data),off 传数字。如果照搬旧文档写成 emitter.emit({ eventId, data }),编译器会直接报错。


六、验证结果

修复后,完整的控制台日志展示了从卡片点击到 Tab 唤醒的全链路:

热启动场景:

复制代码
★ onNewWant 收到 Want 参数: {"targetPage":"MainContainer","startTab":2}
★ EntryBridge.initialTab 已写入: 2
★ 热启动: 通知 MainContainer 切换 Tab → 2
[MainContainer] ★ 热启动: 收到 Tab 切换事件, tabIndex = 2
[MainContainer] ★ Tab 切换前: currentIndex = 0
[MainContainer] ★ Tab 切换完成: currentIndex = 2

路由覆盖矩阵:

主应用状态 触发方法 页面 Tab 来源
未启动(冷启动) onCreate → onWindowStageCreate MainContainer EntryBridge.initialTab
后台运行 onNewWant MainContainer emitter 事件
桌面图标正常启动 onWindowStageCreate MainContainer 无(保持上次 Tab)

七、问题与解决办法汇总

问题 现象 根因 解决办法
服务卡片点击后无法返回首页 返回键直接退出 App 卡片直接加载独立 KitchenDevicePage,MainContainer 未入栈 卡片跳转目标改为 MainContainer + startTab 参数
再次点击桌面图标仍打开厨电页 App 从后台恢复时停留在厨电页 系统记住了被截断的页面栈 统一入口后,页面栈完整,系统记忆正常
热启动时 Tab 未切换 点击卡片但停留在之前 Tab onNewWant 中 pendingTargetPage 判断条件过严 无条件提取 startTab 并发送 emitter 通知
emitter.emit 报类型错误 data does not exist in type InnerEvent API 23 中 emit 的 data 需放在第2参数 改为 emit({ eventId }, { data: {...} })
从其他 Tab 退后台再点卡片不切换 卡片点击无反应 onNewWant 中 pendingTargetPage 为空,跳过了 emitter 通知 独立判断 startTab,不依赖 pendingTargetPage

八、本阶段总结

这次修复从发现"回不去"的幽灵 Bug 开始,深入到 HarmonyOS 页面栈机制,最终通过统一跳转入口、静态变量桥梁和 emitter 事件通知,实现了服务卡片跳转的冷启动、热启动、后台唤起全场景覆盖。

核心收获:

  • 不要用 loadContent 直接加载独立页面------这会截断页面栈
  • 服务卡片统一跳转到主容器,通过参数指定 Tab
  • 冷启动用静态变量传参,热启动用 emitter 通知
  • emitter.emit 的 data 必须放在第2参数 ,不能与 eventId 混在同一个对象里

📚 本系列持续更新中:下一篇我们将深入更复杂的业务场景,探索鸿蒙底层的多线程并发与性能调优。
🔗 专栏入口:《HarmonyOS6.1全场景实战》合集
📦 获取基线版本源码包 :包括第1-15篇所有代码 + 架构文档 + Flask 后端
如果你觉得这篇"排雷指南"对您有所帮助,麻烦您动动发财之手点赞 👍、收藏 ⭐ 和评论 💬。谢谢大家支持!!

纯血鸿蒙,踩坑填坑。我们下一篇见!

相关推荐
若兰幽竹4 个月前
HarmonyOS 6.1 开发者实战 | 《灵犀厨房》多设备烹饪并发:从反复踩坑到架构重构
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(番外篇):【打包上架】三模块一体化工程的 Release 包构建与元服务独立分发
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(三十三):权限管理——用一套“安检系统”告别散装代码
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(三十二):【数据一致性】个人档案的“三重持久化”修复——让偏好、健康与头像真正同步
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
HarmonyOS 6.1 开发者盛宴|《灵犀厨房》实战(三十):【社区分享】本地社区功能——让菜谱从“独享”走向“共享”
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(二十九):【偏好持久化】偏好设置与推荐引擎联动——让 App 越用越“懂你”
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(二十七):告别 UI 冻结——使用 TaskPool 实现高性能并发图像分析
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(二十八):【数据持久化】收藏与浏览历史——让数据在 App 重启后依然“活着”
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
HarmonyOS 6.1 全场景实战|《灵犀厨房》实战(二十六):【响应式布局】折叠屏与平板完美适配——一套代码,多端呈现
华为鸿蒙系统·灵犀厨房·harmonyos6.1
若兰幽竹4 个月前
【HarmonyOS 6.1 全场景实战】《灵犀厨房》实战(二十五):【深色模式】一键切换暗色主题——让 App 在深夜也温柔
华为鸿蒙系统·灵犀厨房·harmonyos6.1