适合谁看

  • 正在调试鸿蒙 MethodChannel 插件的人

  • 遇到"页面没反应但原生好像跑了"的人

  • 不想把日志打成一团的人

问题背景

鸿蒙插件调试比普通页面调试复杂,因为你面对的是两层运行时:

  • Flutter/Dart

  • ArkTS/鸿蒙原生侧

如果日志层次不分清,很快就会陷入:这条日志是谁打的?问题到底出在哪一层?

项目中的真实场景

食界探味当前的日志分布:

文件

日志内容

SpeechRecognitionPlugin.ets

ArkTS

引擎创建、权限申请、识别回调

TextToSpeechPlugin.ets

ArkTS

引擎创建、播报回调、停止

AntiPeepProtectionPlugin.ets

ArkTS

订阅状态、蒙层设置

IntentNavigationPlugin.ets

ArkTS

入口推送、pending 缓存

speech_recognition_channel.dart

Flutter

通道调用结果

intent_navigation_channel.dart

Flutter

路由跳转、pending 消费

anti_peep_protection_channel.dart

Flutter

事件接收、状态变化

ai_explore_coordinator.dart

Flutter

语音识别结果、AI 提交

核心实现

一、ArkTS 侧负责记录原生能力真实状态

鸿蒙原生侧更适合记录:

日志类型

说明

示例

权限申请结果

用户是否授权

requestPermission failed

引擎创建结果

TTS/ASR 引擎是否创建成功

TTS engine created successfully

系统状态变化

防窥状态、事件触发

Anti-peep status HIDE

监听器回调

回调是否触发

onStart requestId: xxx

错误详情

原生错误码和信息

onError code: xxx, msg: xxx

SpeechRecognitionPlugin 的日志:

// SpeechRecognitionPlugin.ets

// 引擎创建
console.info(TAG, 'Engine created successfully');

// 权限申请
console.error(TAG, `requestPermission failed: ${JSON.stringify(err)}`);

// 识别回调
console.info(TAG, `onResult: ${JSON.stringify(result)}`);
console.info(TAG, `onComplete sessionId: ${sessionId}`);
console.error(TAG, `onError code: ${errorCode}, msg: ${errorMessage}`);

// 引擎销毁
console.info(TAG, 'Engine shutdown');

TextToSpeechPlugin 的日志:

// TextToSpeechPlugin.ets

// 引擎创建
console.info(TAG, 'TTS engine created successfully');

// 播报回调
console.info(TAG, `onStart requestId: ${requestId}`);
console.info(TAG, `onComplete requestId: ${requestId}`);
console.info(TAG, `onStop requestId: ${requestId}`);
console.error(TAG, `onError requestId: ${requestId}, code: ${errorCode}, msg: ${errorMessage}`);

// 播报调用
console.info(TAG, 'speak called');

// 引擎销毁
console.info(TAG, 'TTS engine shutdown');

AntiPeepProtectionPlugin 的日志:

// AntiPeepProtectionPlugin.ets

// 订阅状态
console.info(TAG, 'Anti-peep status subscription registered');
console.info(TAG, 'Anti-peep status subscription removed');

// 事件触发
console.info(TAG, 'Anti-peep status PASS');
console.warning(TAG, 'Anti-peep status HIDE');

// 蒙层设置
console.info(TAG, 'Anti-peep mask layer shown');

// 设置请求
console.info(TAG, 'Anti-peep option dialog already requested');

二、Flutter 侧负责记录"页面最终拿到了什么"

Flutter 侧更适合记录:

日志类型

说明

示例

通道调用是否成功

MethodChannel 结果

consumePendingNavigation failed

页面是否收到事件

事件处理

Anti-peep event: HIDE

路由是否真正跳转

路由日志

Intent navigation: pageId="search"

UI 状态是否被更新

状态变化

AI助手 对话出错: xxx

业务逻辑结果

最终行为

AI助手 工具调用: search_dishes(...)

intent_navigation_channel.dart 的日志:

// intent_navigation_channel.dart

// 路由跳转
AppLogger.info(
  'Intent navigation: pageId="${payload.pageId}" -> route="$route"',
);

// pending 消费
AppLogger.warning('consumePendingNavigation failed: $e');

anti_peep_protection_channel.dart 的日志:

// anti_peep_protection_channel.dart

// 事件接收
if (event == 'HIDE') {
  AppLogger.warning(
    '$message - possible anti-peek trigger detected on collection screen',
  );
} else {
  AppLogger.info(message);
}

ai_explore_coordinator.dart 的日志:

// ai_explore_coordinator.dart

// 工具调用
AppLogger.info('[AI助手] 工具调用: ${toolCall.name}(${toolCall.arguments})');

// 错误
AppLogger.error('[AI助手] 对话出错: $e');
AppLogger.error('[AI助手] 语音识别出错: $e');
AppLogger.error('[AI助手] TTS 出错: $e');

三、两边日志不该重复写同一件事

如果 ArkTS 已经记录了"引擎启动成功",Flutter 侧就没必要再重复假装记录这件事。Flutter 应该记录的是"最终拿到了什么返回值"。

正确的日志分工:

事件

ArkTS 记录

Flutter 记录

TTS 引擎创建

TTS engine created successfully

不记录(没必要)

TTS 播报完成

onComplete requestId: xxx

不记录(没必要)

TTS 播报调用

speak called

不记录(没必要)

Flutter 收到结果

不记录(ArkTS 不知道)

不记录(正常流程)

Flutter 路由跳转

不记录

Intent navigation: pageId="search"

错误场景的日志分工:

事件

ArkTS 记录

Flutter 记录

TTS 引擎创建失败

Failed to create TTS engine: xxx

Channel call failed: speak

TTS 播报出错

onError code: xxx, msg: xxx

TTS 出错: xxx

语音识别失败

onError code: xxx, msg: xxx

语音识别出错: xxx

权限拒绝

requestPermission failed

语音识别出错,请手动输入

四、调试时最好围绕链路打点

以语音识别为例,完整的链路日志:

用户点击语音按钮
  │
  ├─ Flutter: [AI助手] 开始语音输入
  │
  ├─ Flutter: SpeechRecognitionChannel.startListening()
  │
  ├─ ArkTS: requestMicrophonePermission()
  │   ├─ 成功: (无日志,正常流程)
  │   └─ 失败: requestPermission failed: xxx
  │
  ├─ ArkTS: createEngine()
  │   ├─ 成功: Engine created successfully
  │   └─ 失败: Failed to create engine: xxx
  │
  ├─ ArkTS: startListening()
  │   └─ console.info: startListening
  │
  ├─ ArkTS: onResult()
  │   └─ console.info: onResult: {result: "想吃鸡蛋", isLast: true}
  │
  ├─ ArkTS: onComplete()
  │   └─ console.info: onComplete sessionId: xxx
  │
  ├─ ArkTS: shutdownEngine()
  │   └─ console.info: Engine shutdown
  │
  ├─ Flutter: 收到文本 "想吃鸡蛋"
  │
  ├─ Flutter: [AI助手] 工具调用: search_dishes(...)
  │
  └─ Flutter: [AI助手] 对话完成

按这条链路逐层观察,就能快速定位问题出在哪一层。

五、日志级别建议

级别

ArkTS 使用场景

Flutter 使用场景

info

引擎创建成功、回调触发

路由跳转、事件接收

warning

状态异常(如防窥 HIDE)

通道调用失败(如 MissingPluginException)

error

引擎创建失败、识别出错

对话出错、TTS 出错

不要把 info 级别的日志打太多——生产环境会很吵。warning 和 error 才是真正需要关注的。

关键代码位置

文件

日志内容

app/ohos/entry/src/main/ets/plugins/SpeechRecognitionPlugin.ets

原生日志

app/ohos/entry/src/main/ets/plugins/TextToSpeechPlugin.ets

原生日志

app/ohos/entry/src/main/ets/plugins/AntiPeepProtectionPlugin.ets

原生日志

app/lib/core/platform/intent_navigation_channel.dart

Flutter 日志

app/lib/core/platform/anti_peep_protection_channel.dart

Flutter 日志

app/lib/core/ai/ai_explore_coordinator.dart

Flutter 日志

日志分工总结

ArkTS 侧(原生事实)
  ├─ 权限申请结果
  ├─ 引擎创建成功/失败
  ├─ 系统状态变化
  ├─ 监听器回调触发
  └─ 错误详情(错误码 + 信息)

Flutter 侧(页面结果)
  ├─ 通道调用是否成功
  ├─ 页面是否收到事件
  ├─ 路由是否真正跳转
  ├─ UI 状态是否被更新
  └─ 业务逻辑结果(工具调用、对话完成)

常见坑

  • 所有日志只打在一侧 — 看不到完整链路

  • ArkTS 和 Flutter 两边记录完全重复的信息 — 日志太多,噪音大

  • 日志没有链路上下文 — 看不出是哪次调用

  • 只看"成功"日志,不记录失败和空结果 — 问题出在失败路径

  • 日志级别不分 — info 和 error 混在一起,生产环境很吵

  • 没有 TAG 前缀 — ArkTS 侧多插件日志混在一起

可复用模板

ArkTS 侧日志模板

const TAG = 'YourPlugin';

// 成功
console.info(TAG, 'Engine created successfully');
console.info(TAG, `onComplete requestId: ${requestId}`);

// 失败
console.error(TAG, `Failed to create engine: ${err.message}`);
console.error(TAG, `onError code: ${errorCode}, msg: ${errorMessage}`);

// 状态变化
console.info(TAG, `onResult: ${JSON.stringify(result)}`);

Flutter 侧日志模板

// 成功
AppLogger.info('Intent navigation: pageId="$pageId" -> route="$route"');

// 失败
AppLogger.warning('consumePendingNavigation failed: $e');
AppLogger.error('[AI助手] 对话出错: $e');

// 事件
AppLogger.info('Anti-peep event: $event');

链路日志检查清单

语音识别链路:
  □ ArkTS: 权限申请结果
  □ ArkTS: 引擎创建结果
  □ ArkTS: 识别回调
  □ ArkTS: 引擎销毁
  □ Flutter: 收到文本
  □ Flutter: 提交 AI

TTS 链路:
  □ ArkTS: 引擎创建结果
  □ ArkTS: 播报回调
  □ ArkTS: 播报调用
  □ ArkTS: 引擎销毁

防窥链路:
  □ ArkTS: 订阅状态
  □ ArkTS: 状态变化
  □ ArkTS: 蒙层设置
  □ Flutter: 事件接收
  □ Flutter: 状态更新

本篇总结

调试鸿蒙插件时,Flutter 和 ArkTS 两边都要打日志,但两边记录的内容应该不同:

  • ArkTS 侧 — 记录原生能力真实状态(权限、引擎、回调、错误)

  • Flutter 侧 — 记录页面最终拿到了什么(通道结果、路由跳转、状态更新)

只要按链路打点,定位问题会快很多。两边日志不该重复写同一件事,否则噪音太大。日志级别也要分清——info 记正常流程,warning/error 记异常。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐