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 后端
如果你觉得这篇"排雷指南"对您有所帮助,麻烦您动动发财之手点赞 👍、收藏 ⭐ 和评论 💬。谢谢大家支持!!纯血鸿蒙,踩坑填坑。我们下一篇见!