鸿蒙原生 + Flutter 混合开发:Flutter 的日志为什么打不出来

鸿蒙原生 + Flutter 混合开发:Flutter 的日志为什么打不出来

这篇解决一个很坑人的问题:你在 Flutter 里写了 print,真机上一条都看不到。

不夸张地说,这是混合开发初期最浪费时间的一个坑——代码明明跑通了,就是不知道执行到哪一步。


一、现象:日志"打了,但没打"

你做的事 你以为的结果 实际结果
Flutter 里 print('进来了') DevEco 的 Log 窗口能看到 ❌ 一条都没有
Flutter 里 debugPrint('进来了') 同上 ❌ 一条都没有
原生里 hilog.info(...) 能看到 ✅ 正常输出

结论:在鸿蒙的嵌入式场景下,Dart 侧的 print / debugPrint 不会进入系统日志(hilog)。
而 DevEco Studio 的 Log 窗口显示的就是 hilog,所以你在里面永远搜不到 Flutter 的日志。

注意:这不代表 Flutter 的日志"没产生",而是它没有走到系统日志这条路上。
想看,就必须自己把它送出去。


二、解决思路:借通道把日志送出去

既然原生能写日志,那就让 Flutter 把日志通过通道丢给原生,由原生负责输出。

Flutter:  HybridTrace.log('...')
   │     复用已有的 MethodChannel,方法名固定叫 'trace'
   ▼
原生:     case 'trace' → 用统一前缀写 hilog
   ▼
DevEco Log 窗口 / hdc shell hilog:按前缀一搜就能看到

三个设计要求(都是踩过坑总结的):

  1. 不新开通道——复用项目里已有的 MethodChannel,加一个 trace 方法就行。
  2. 失败必须静默——日志本身绝对不能影响业务,发送失败就吞掉。
  3. 统一前缀——所有日志都带同一个前缀,排查时一个词筛出全链路。

三、Flutter 侧实现

新建一个 lib/hybrid_trace.dart,全局只有这一个入口:

import 'dart:async';
import 'package:flutter/services.dart';

/// 统一诊断日志。
///
/// 为什么不用 print / debugPrint:在鸿蒙嵌入式场景下它们不会进入 hilog,
/// DevEco 的 Log 窗口里一条都看不到。这里统一转发到原生输出。
///
/// 用法:HybridTrace.log('xxx'),失败静默(诊断日志不能影响业务)。
class HybridTrace {
  const HybridTrace._();

  /// 复用业务通道;方法名固定 'trace'
  static const MethodChannel _channel =
      MethodChannel('com.example.hybrid/native_calculator');

  /// 统一前缀:原生侧按这个词过滤
  static const String tag = '[HybridTrace]';

  static void log(String message) {
    unawaited(_send(message));      // 不 await:日志不能拖慢业务
  }

  static Future<void> _send(String message) async {
    try {
      await _channel.invokeMethod<void>('trace', <String, Object?>{
        'message': message,
      });
    } catch (_) {
      // 发送失败直接吞掉:诊断日志绝不影响业务
    }
  }
}

调用处就和写 print 一样简单:

HybridTrace.log('subtract first=$first second=$second');
await _nativeChannel.invokeMethod<dynamic>('subtract', {'first': 12, 'second': 5});
HybridTrace.log('subtract 返回 $result');

四、原生侧实现

在已有的 setMethodCallHandler 里加一个分支就行(关键是那个统一前缀):

// 统一前缀,和 Dart 侧对齐
const TAG_TRACE = '[HybridTrace]';

channel.setMethodCallHandler({
  onMethodCall: (call, result) => {
    // ① 诊断日志入口
    if (call.method === 'trace') {
      hilog.info(0x0000, 'HybridDemo', '%{public}s, %{public}s',
        TAG_TRACE, call.argument('message') as string);
      result.success(null);      // ⚠️ 必须回一次,否则 Flutter 侧一直等
      return;
    }

    if (call.method === 'subtract') {
      // ② 顺手把每次调用也记一条,排查时能看出"调到了没"
      hilog.info(0x0000, 'HybridDemo', '%{public}s, ←Dart %{public}s',
        TAG_TRACE, call.method);
      // ... 业务逻辑
      result.success(argInt(call, 'first') - argInt(call, 'second'));
      return;
    }

    result.notImplemented();
  }
});

五、iOS / Android 也一样

这套做法是平台无关的:只要原生侧把 trace 方法实现出来,Flutter 代码一行都不用改。

平台 输出方式 代码
HarmonyOS hilog hilog.info(domain, tag, '%{public}s, %{public}s', PREFIX, msg)
iOS os_log os_log("%{public}@", log: .default, type: .info, "\(PREFIX) \(msg)")
Android Logcat Log.i("HybridTrace", msg)

记住一句话:同一套 Dart 代码,靠原生各自的实现落到各自的日志系统里。


六、日志格式:四段式,能拼出全链路

光有日志还不够,能看出"卡在哪一段"才是关键。约定一个四段式格式:

环节 谁打 格式 示例
Flutter 发起调用 Flutter(在通道封装里统一打) →native <方法名> {参数} →native subtract {first: 12, second: 5}
原生收到调用 原生(方法分发入口) ←Dart <方法名> ←Dart subtract
原生推事件 原生(事件发送处) →Dart emitEvent(<类型>) →Dart emitEvent(deviceInfo)
Flutter 收到事件 Flutter(事件监听里统一打) ←native event <类型> {载荷} ←native event deviceInfo {...}

真机日志长这样(按前缀一筛就出来):

[HybridTrace], →native subtract {first: 12, second: 5}
[HybridTrace], ←Dart subtract
[HybridTrace], →Dart emitEvent(deviceInfo)
[HybridTrace], ←native event deviceInfo {deviceId: 3AQ0...}

一眼就能判断:"原生日志有、Dart 的 →native 没有" → 是 Flutter 没调到;"←Dart 有、事件没有" → 是原生处理中出了问题。

最省事的做法:在通道封装的最底层(所有调用都经过的那个方法)统一打第一条和第四条,业务代码里就不用重复写了。


七、怎么抓日志

平台 命令 / 位置
HarmonyOS hdc shell hilog | grep HybridTrace
HarmonyOS(IDE) DevEco Studio 的 Log 窗口,过滤框输入 HybridTrace
Android adb logcat | grep HybridTrace
iOS Console.app 或 Xcode 控制台,过滤 HybridTrace

八、几个注意事项

注意点 说明
别打隐私 hilog 的格式串区分 %{public}s 和 %{private}s。设备号、文件路径、用户标识这类内容,正式包建议用 %{private}s
别打超大对象 单个日志行有长度上限,会被截断。要打列表就只打数量、标识和状态,不要 JSON.stringify(整个列表)
失败要静默 日志发送是 try/catch 吞异常的,绝不能因为"日志失败"让业务挂掉
不要每次调用都 await 用 unawaited 发出去就行,日志不该拖慢业务
前缀要唯一 别用 log、debug 这种通用词,否则过滤时满屏噪音
原生必须回结果 trace 分支也要 result.success(null),否则 Flutter 侧那个 await 会一直挂着

九、自检清单

posted @ 2026-09-15 17:26  带头大哥d小弟  阅读(4)  评论(0)    收藏  举报