鸿蒙 Flutter 项目的调试工具链:hdc 日志抓取、ArkTS 侧断点与 Flutter DevTools 的协同排查
适合谁看
-
正在做 Flutter 鸿蒙项目调试但不知道从何入手的开发者
-
遇到"MethodChannel 调用失败但不知道哪层出错"问题的人
-
想提高 Flutter 鸿蒙项目调试效率的开发者
问题背景
Flutter 鸿蒙项目的错误链路比纯 Flutter 项目长得多:
Flutter Widget → Flutter 业务逻辑 → MethodChannel → ArkTS Plugin → 鸿蒙系统 API
任何一个环节出错,都可能导致功能异常。但错误信息可能只在某一层可见:
-
Flutter 侧的
MissingPluginException只说明"原生插件不可用" -
ArkTS 侧的
console.error只在 DevEco Studio 或 hdc 日志中可见 -
系统 API 的错误可能被 ArkTS 侧吞掉
调试的关键在于:同时看多层日志,时间对齐,找到真正的错误源头。
项目中的真实场景
食界探味的典型调试场景:
-
语音识别不工作:Flutter 调用了
startListening,但没有收到结果 -
卡片跳转失败:点击桌面卡片,Flutter 页面没有跳转
-
防窥保护异常:激活防窥后,Flutter 遮罩没有显示
每个场景都需要跨层排查。
核心实现
工具一:hdc 日志抓取
hdc(HarmonyOS Device Connector)是鸿蒙的设备调试工具,可以抓取系统日志:
# 查看所有日志
hdc hilog
# 过滤特定 TAG
hdc hilog | grep "SpeechRecognitionPlugin"
# 过滤错误日志
hdc hilog | grep -E "ERROR|error"
# 保存日志到文件
hdc hilog > debug.log
ArkTS 侧的日志输出:
// ArkTS 侧的日志
console.info(TAG, 'Engine created successfully');
console.error(TAG, `Failed to create engine: ${err.message}`);
console.warn(TAG, `channel not ready, storing pageId "${pageId}" as pending`);
hdc 日志的格式:
06-22 10:30:15.123 1234 5678 I C00000: SpeechRecognitionPlugin: Engine created successfully
06-22 10:30:15.456 1234 5678 E C00000: SpeechRecognitionPlugin: Failed to create engine: timeout
格式:日期 时间 PID TID 级别 TAG: 消息
工具二:DevEco Studio 断点调试
DevEco Studio 支持对 ArkTS 代码进行断点调试:
-
在 ArkTS 代码中设置断点
-
启动调试模式(Run → Debug)
-
触发断点后查看变量值
关键断点位置:
// SpeechRecognitionPlugin.ets - 建议设置断点的位置
private async handleStartListening(call: MethodCall, result: MethodResult): Promise<void> {
// 断点 1:检查 Flutter 侧传来的参数
this.pendingResult = result;
const hasPermission = await this.requestMicrophonePermission();
// 断点 2:检查权限申请结果
if (!hasPermission) {
// ...
}
try {
await this.createEngine();
// 断点 3:检查引擎是否创建成功
this.setupListener();
this.startListening();
} catch (err) {
// 断点 4:捕获异常
}
}
工具三:Flutter DevTools
Flutter DevTools 是 Flutter 官方的调试工具,可以在鸿蒙设备上使用:
# 启动 Flutter DevTools
flutter run --observatory-port=8080
# 在浏览器中打开 DevTools
# http://127.0.0.1:8080
Flutter DevTools 的关键功能:
-
Widget Inspector:查看 Widget 树和布局
-
Network:查看网络请求
-
Logging:查看 Flutter 日志
-
Performance:查看帧率和性能
协同排查方法
场景一:语音识别不工作
排查步骤:
# 1. 查看 Flutter 侧日志
# 在 Flutter 代码中添加日志
AppLogger.info('Starting speech recognition...');
# 2. 查看 ArkTS 侧日志
hdc hilog | grep "SpeechRecognition"
# 3. 检查权限状态
hdc hilog | grep "permission"
# 4. 检查引擎状态
hdc hilog | grep "Engine"
时间对齐示例:
# Flutter 侧日志(10:30:15.100)
Starting speech recognition...
# ArkTS 侧日志(10:30:15.150)
SpeechRecognitionPlugin: attached to engine
# ArkTS 侧日志(10:30:15.200)
SpeechRecognitionPlugin: requestPermission failed: ...
# 结论:权限申请失败,导致引擎未创建
场景二:卡片跳转失败
排查步骤:
# 1. 查看卡片点击事件
hdc hilog | grep "DailyRecommendCard"
# 2. 查看 EntryAbility 接收的 Want 参数
hdc hilog | grep "EntryAbility"
# 3. 查看 IntentNavigationPlugin 的日志
hdc hilog | grep "IntentNavigation"
# 4. 查看 Flutter 侧路由日志
# 在 Flutter 代码中检查 IntentNavigationChannel 的日志
时间对齐示例:
# ArkTS 侧日志(10:30:20.100)
DailyRecommendCard: openDishDetail called
# ArkTS 侧日志(10:30:20.150)
EntryAbility: onCreate, pageId: dish_detail, dishId: beef-curry-mok1r6xe-6jhu
# ArkTS 侧日志(10:30:20.200)
IntentNavigationPlugin: channel not ready, storing as pending
# Flutter 侧日志(10:30:20.300)
IntentNavigationChannel: consumePendingNavigation received
# Flutter 侧日志(10:30:20.350)
IntentNavigationChannel: navigating to /dish/beef-curry-mok1r6xe-6jhu
# 结论:正常流程,如果页面没跳转,检查 GoRouter 配置
场景三:MethodChannel 调用失败
排查步骤:
# 1. 检查 Flutter 侧是否调用了 invokeMethod
# 在 Flutter 代码中添加日志
AppLogger.info('Calling method: $method');
# 2. 检查 ArkTS 侧是否收到了调用
hdc hilog | grep "onMethodCall"
# 3. 检查 ArkTS 侧是否返回了结果
hdc hilog | grep "result.success\|result.error"
# 4. 检查 Flutter 侧是否收到了结果
时间对齐示例:
# Flutter 侧日志(10:30:25.100)
Calling method: startListening
# ArkTS 侧日志(无)
# 结论:MethodChannel 未连接,插件未注册
# 或者:
# ArkTS 侧日志(10:30:25.150)
SpeechRecognitionPlugin: onMethodCall: startListening
# ArkTS 侧日志(10:30:25.200)
SpeechRecognitionPlugin: Engine created successfully
# Flutter 侧日志(10:30:25.300)
Speech recognition completed: 牛肉咖喱
# 结论:正常流程
日志规范
为了让多层日志更容易对齐,建议统一日志格式:
// Flutter 侧日志规范
class AppLogger {
static void info(String message) {
print('[Flutter][INFO] $message');
}
static void warning(String message) {
print('[Flutter][WARN] $message');
}
static void error(String message) {
print('[Flutter][ERROR] $message');
}
}
// ArkTS 侧日志规范
const TAG = 'SpeechRecognitionPlugin';
console.info(TAG, `[ArkTS][INFO] Engine created`);
console.error(TAG, `[ArkTS][ERROR] Failed: ${err.message}`);
console.warn(TAG, `[ArkTS][WARN] Channel not ready`);
日志格式:[层级][级别] 消息
常见调试技巧
技巧一:MethodChannel 双向日志
在 MethodChannel 的两端都添加日志:
// Flutter 侧
static Future<String> startListening() async {
AppLogger.info('[Channel] Calling startListening');
final result = await _channel.invokeMethod<String>('startListening');
AppLogger.info('[Channel] startListening result: $result');
return result ?? '';
}
// ArkTS 侧
onMethodCall(call: MethodCall, result: MethodResult): void {
console.info(TAG, `[Channel] Received: ${call.method}`);
switch (call.method) {
case 'startListening':
this.handleStartListening(call, result);
break;
}
}
技巧二:时间戳对齐
在日志中添加时间戳:
// Flutter 侧
final timestamp = DateTime.now().millisecondsSinceEpoch;
AppLogger.info('[$timestamp] Calling startListening');
// ArkTS 侧
const timestamp = Date.now();
console.info(TAG, `[$timestamp] Received: ${call.method}`);
技巧三:错误码标准化
在 ArkTS 侧使用标准化的错误码:
// 错误码定义
enum ErrorCode {
PERMISSION_DENIED = 'PERMISSION_DENIED',
ENGINE_CREATE_FAILED = 'ENGINE_CREATE_FAILED',
CHANNEL_NOT_READY = 'CHANNEL_NOT_READY',
}
// 使用
result.error(ErrorCode.PERMISSION_DENIED, '麦克风权限被拒绝', null);
Flutter 侧根据错误码决定处理策略:
try {
await _channel.invokeMethod('startListening');
} on PlatformException catch (e) {
switch (e.code) {
case 'PERMISSION_DENIED':
_showPermissionGuide();
break;
case 'ENGINE_CREATE_FAILED':
_showRetryDialog();
break;
default:
_showGenericError();
}
}
关键代码位置
-
app/ohos/entry/src/main/ets/plugins/— ArkTS 侧插件日志 -
app/lib/core/platform/— Flutter 侧 Channel 日志 -
app/lib/core/utils/logger.dart— Flutter 日志工具 -
app/ohos/entry/src/main/ets/entryability/EntryAbility.ets— Ability 生命周期日志
鸿蒙侧实现
鸿蒙侧的调试工具:
-
hdc hilog:抓取系统日志
-
DevEco Studio:ArkTS 代码断点调试
-
console.log/info/error:代码内日志输出
hdc 常用命令:
# 查看设备连接状态
hdc list targets
# 安装应用
hdc install app.hap
# 启动应用
hdc shell aa start -a EntryAbility -b com.foodvoyage
# 查看日志
hdc hilog | grep "TAG"
# 清除日志
hdc hilog -r
Flutter 侧实现
Flutter 侧的调试工具:
-
Flutter DevTools:Widget Inspector、Network、Logging
-
print/AppLogger:代码内日志输出
-
flutter run --debug:调试模式运行
Flutter DevTools 的关键功能:
-
Widget Inspector:查看 Widget 树,检查布局问题
-
Performance:查看帧率,检测性能瓶颈
-
Network:查看网络请求,检查 API 调用
-
Logging:查看 Flutter 日志,时间线对齐
常见坑
-
坑 1:hdc 日志太多,找不到关键信息。使用
grep过滤特定 TAG,或使用--pid过滤特定进程。 -
坑 2:ArkTS 断点不生效。检查 DevEco Studio 是否正确连接到设备,断点是否在可执行代码行上。
-
坑 3:Flutter DevTools 连接失败。检查端口是否被占用,设备是否正确连接。
-
坑 4:时间戳不一致。Flutter 和 ArkTS 的系统时间可能有微差,需要用相对时间对齐。
-
坑 5:日志被系统吞掉。某些系统级别的错误不会输出到 hilog,需要查看系统 dump 信息。
可复用模板
// Flutter 侧 - 调试日志封装模板
class DebugLogger {
static const bool _debugMode = kDebugMode;
static final List<String> _logs = [];
static void log(String tag, String message) {
if (!_debugMode) return;
final timestamp = DateTime.now().toIso8601String();
final log = '[$timestamp][$tag] $message';
_logs.add(log);
print(log);
}
static void dump() {
for (final log in _logs) {
print(log);
}
}
static List<String> getLogs() => List.unmodifiable(_logs);
}
// 鸿蒙侧 - 调试日志封装模板
const DEBUG_MODE = true;
function debugLog(tag: string, message: string): void {
if (!DEBUG_MODE) return;
const timestamp = Date.now();
console.info(tag, `[$timestamp] $message`);
}
function debugError(tag: string, message: string): void {
if (!DEBUG_MODE) return;
const timestamp = Date.now();
console.error(tag, `[$timestamp] $message`);
}
本篇总结
Flutter 鸿蒙项目的调试,核心是"三方协同":hdc 抓取 ArkTS 侧日志、DevEco Studio 断点调试 ArkTS 代码、Flutter DevTools 调试 Flutter 代码。排查问题的关键是"时间对齐"——在同一时间点查看三层日志,找到真正的错误源头。建立"先查 Flutter 日志 → 再查 ArkTS 日志 → 最后查系统日志"的排查顺序,可以大大提高调试效率。
更多推荐





所有评论(0)