11 · 鸿蒙特有 API 调用:通过 MethodChannel 接入 ⭐

阶段:阶段四 · 鸿蒙端深度适配(专栏核心) | 篇号:11 / 25 |


开篇

上一篇讲了桥接原理,这一篇动手写代码——我们要打通一个完整链路:

Dart 调用 → MethodChannel → 鸿蒙 ArkTS 原生 API → 返回结果给 Dart

并以「获取鸿蒙设备信息 + 接入华为推送」两个实战案例,把跨端调用的标准模式彻底掌握。这是后续做任何鸿蒙原生功能的基础。

本篇你将收获

  1. ✅ 完整跑通一次「Dart → ArkTS → Dart」调用闭环
  2. ✅ 学会鸿蒙常用 API(设备信息 / 推送 / 振动)的接入方式
  3. ✅ 掌握「条件编译」:让代码只在鸿蒙端执行
  4. ✅ 写出可测试、可复用的 Channel Service 封装

一、案例 1:获取鸿蒙设备信息(最小可跑 demo)

1.1 Dart 端定义 Service

新建
lib/services/harmony_device_service.dart:

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

/// 鸿蒙设备信息服务
///
/// 仅在鸿蒙端调用有效;其他平台调用会抛 [PlatformException]。
class HarmonyDeviceService {
  static const _channel = MethodChannel('com.example.app/device');

  /// 是否运行在 HarmonyOS
  static bool get isHarmonyOS => Platform.operatingSystem == 'ohos';

  /// 获取设备品牌(如 'HUAWEI')
  static Future<String> getBrand() async {
    if (!isHarmonyOS) return 'Unknown';
    final brand = await _channel.invokeMethod<String>('getBrand');
    return brand ?? 'Unknown';
  }

  /// 获取系统版本(如 'HarmonyOS NEXT 5.0.1')
  static Future<String> getOsVersion() async {
    if (!isHarmonyOS) return 'Unknown';
    return await _channel.invokeMethod<String>('getOsVersion') ?? 'Unknown';
  }

  /// 获取设备唯一标识(需权限 ohos.permission.appId)
  static Future<String?> getDeviceId() async {
    if (!isHarmonyOS) return null;
    return await _channel.invokeMethod<String>('getDeviceId');
  }
}

设计要点

  • isHarmonyOS 守卫,避免在其他平台调用
  • 所有方法返回 Future<T>,配合 Dart 异步
  • 默认值处理(null 安全)

1.2 鸿蒙端实现(ArkTS)

修改
ohos/entry/src/main/ets/entryability/EntryAbility.ets:

import UIAbility from '@ohos.app.ability.UIAbility';
import deviceInfo from '@ohos.deviceInfo';
import { FlutterAbilityDelegate, FlutterAbilityDelegateNew } from '@ohos.flutter';
import { MethodChannel, MethodCallResult, MethodCall } from '@ohos.flutter';

const CHANNEL_NAME = 'com.example.app/device';

class DeviceMethodHandler {
  async onMethodCall(call: MethodCall, result: MethodCallResult) {
    switch (call.method) {
      case 'getBrand':
        result.success(deviceInfo.brand);   // 如 'HUAWEI'
        break;
      case 'getOsVersion':
        const version = `${deviceInfo.osFullName} ${deviceInfo.sdkApiVersion}`;
        result.success(version);
        break;
      case 'getDeviceId':
        // 需在 module.json5 声明 ohos.permission.appId
        result.success(deviceInfo.udid);
        break;
      default:
        result.notImplemented();
    }
  }
}

export default class EntryAbility extends UIAbility {
  private delegate: FlutterAbilityDelegateNew;

  onCreate(want, launchParam) {
    this.delegate = new FlutterAbilityDelegateNew(this);
    this.delegate.onCreate(want, launchParam);

    // ★ 注册 MethodChannel
    const handler = new DeviceMethodHandler();
    this.delegate.addMethodChannel(CHANNEL_NAME, handler);
  }

  onWindowStageCreate(windowStage) {
    this.delegate.onWindowStageCreate(windowStage);
  }

  // ... 其他生命周期方法委托给 delegate
}

关键 API

  • deviceInfo.brand / osFullName / udid 来自鸿蒙 @ohos.deviceInfo 模块
  • result.success(value) 返回成功结果
  • result.notImplemented() 表明方法不存在
  • result.error(code, msg, details) 返回错误

1.3 在 UI 中使用

import 'package:flutter/material.dart';
import 'services/harmony_device_service.dart';

class DeviceInfoPage extends StatefulWidget {
  @override
  State<DeviceInfoPage> createState() => _DeviceInfoPageState();
}

class _DeviceInfoPageState extends State<DeviceInfoPage> {
  String _brand = '';
  String _osVersion = '';

  @override
  void initState() {
    super.initState();
    _loadDeviceInfo();
  }

  Future<void> _loadDeviceInfo() async {
    final brand = await HarmonyDeviceService.getBrand();
    final osVersion = await HarmonyDeviceService.getOsVersion();
    setState(() {
      _brand = brand;
      _osVersion = osVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('设备信息')),
      body: ListTile(
        title: Text('品牌: $_brand'),
        subtitle: Text('系统: $_osVersion'),
      ),
    );
  }
}

1.4 运行验证

flutter run -d ohos

应能看到「品牌: HUAWEI / 系统: HarmonyOS NEXT 5.x」。


二、案例 2:接入华为推送

鸿蒙的推送服务(HarmonyOS Push Kit)是 ArkTS API,Flutter 需通过 Channel 调用。

2.1 配置依赖

ohos/entry/oh-package.json5:

{
  "dependencies": {
    "@hms-core/push-ohos": "^6.5.0"
  }
}

执行:

cd ohos && ohpm install --all

2.2 Dart 端封装

class HarmonyPushService {
  static const _channel = MethodChannel('com.example.app/push');

  /// 获取推送 token
  static Future<String?> getToken() async {
    if (!HarmonyDeviceService.isHarmonyOS) return null;
    return await _channel.invokeMethod<String>('getPushToken');
  }

  /// 监听推送消息流
  static const _eventChannel = EventChannel('com.example.app/push/events');
  static Stream<Map> get messages =>
      _eventChannel.receiveBroadcastStream().map((e) => Map.from(e));
}

2.3 鸿蒙端实现

import push from '@hms-core/push-ohos';
import { MethodChannel, EventChannel } from '@ohos.flutter';

const METHOD_CHANNEL = 'com.example.app/push';
const EVENT_CHANNEL = 'com.example.app/push/events';

class PushMethodHandler {
  async onMethodCall(call: MethodCall, result: MethodCallResult) {
    switch (call.method) {
      case 'getPushToken':
        try {
          const token = await push.getPushToken();
          result.success(token);
        } catch (e) {
          result.error('PUSH_ERROR', e.message, null);
        }
        break;
      default:
        result.notImplemented();
    }
  }
}

// 在 EntryAbility.onCreate 里注册:
this.delegate.addMethodChannel(METHOD_CHANNEL, new PushMethodHandler());

// 注册 EventChannel 推送消息流
this.delegate.addEventChannel(EVENT_CHANNEL, (sink) => {
  push.on('pushMessageReceived', (msg) => {
    sink.success({
      title: msg.title,
      content: msg.content,
      extras: msg.extras,
    });
  });
});

2.4 在 UI 中监听

@override
void initState() {
  super.initState();
  if (HarmonyDeviceService.isHarmonyOS) {
    HarmonyPushService.messages.listen((msg) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('收到推送: ${msg['title']}')),
      );
    });
  }
}

三、条件编译:让代码区分平台

Dart 没有 C 那种 #ifdef,但有等价方案:

import 'dart:io' show Platform;

if (Platform.isAndroid) { ... }
if (Platform.isIOS) { ... }
if (Platform.operatingSystem == 'ohos') { ... }   // 鸿蒙判断

更优雅的封装:

class PlatformUtil {
  static bool get isHarmonyOS => Platform.operatingSystem == 'ohos';
  static bool get isMobile => Platform.isAndroid || Platform.isIOS || isHarmonyOS;
}

⚠️ 不要在 lib/ 里硬编码平台分支,抽象成 Service 接口,各平台提供不同实现。


四、Service 抽象模式(推荐架构)

对每个原生能力,定义一个抽象接口 + 平台实现

// 1. 抽象接口
abstract class DeviceInfoService {
  Future<String> getBrand();
  Future<String> getOsVersion();
}

// 2. Dart 默认实现(非鸿蒙端用)
class FallbackDeviceInfoService implements DeviceInfoService {
  Future<String> getBrand() async => 'Unknown';
  Future<String> getOsVersion() async => 'Unknown';
}

// 3. 鸿蒙实现
class HarmonyDeviceInfoService implements DeviceInfoService { ... }

// 4. 工厂注入
DeviceInfoService get deviceInfoService =>
    Platform.operatingSystem == 'ohos'
        ? HarmonyDeviceInfoService()
        : FallbackDeviceInfoService();

业务代码只依赖 DeviceInfoService 接口,完全感受不到平台差异。配合 Riverpod:

final deviceInfoProvider = Provider<DeviceInfoService>((ref) {
  return deviceInfoService;
});

五、调试 Channel 的工具

5.1 打印所有 Channel 调用

在 Dart 端加一个全局拦截:

const _channels = ['com.example.app/device', 'com.example.app/push'];

void setupChannelLogging() {
  for (final name in _channels) {
    const MethodChannel(name).setMethodCallHandler((call) async {
      debugPrint('[Channel $name] → ${call.method} ${call.arguments}');
      return null;
    });
  }
}

5.2 鸿蒙端 HiLog

在 ArkTS 用 HiLog 输出日志(详见 13 · 鸿蒙端调试技巧):

import hilog from '@ohos.hilog';
hilog.info(0x0001, 'DeviceChannel', `method=${call.method}`);

六、常见问题

Q1:MissingPluginException在鸿蒙端

注册时机错了。必须在 onCreate 里注册,不能放在 onWindowStageCreate。

Q2:result.success调用后 Dart 还是 await 不到

检查 channel name 是否 Dart / ArkTS 完全一致(区分大小写)。

Q3:能传复杂对象吗

不能直接传。序列化为 Map / List

await channel.invokeMethod('setUser', {
  'id': 1,
  'name': 'Alice',
  'tags': ['a', 'b'],
});

Q4:异步任务怎么处理

case 'longTask':
  doAsyncWork().then(result.success).catch(e => result.error('X', e.message, null));
  break;

不要 await,立即返回,结果通过 success 回调。

Q5:Channel 调用很慢怎么排查

# 开启 Flutter 时间线
flutter run --profile --trace-systrace

打开 DevTools → Performance → 看 Main 线程的 Platform Channel 节段。

Q6:如何在 Android / iOS 也实现一份

为每个平台写一份 Service 实现(Kotlin / Swift),保持 Dart 接口一致。可以用 Pigeon 自动生成各端样板代码。

Q7:Channel 名字有规范吗

提议格式:<反向域名>/<功能>,如 com.example.app/battery。避免冲突。


七、小结

步骤

文件

1. Dart Service

lib/services/xxx_service.dart

2. ArkTS Handler

ohos/entry/…/EntryAbility.ets

3. UI 调用

业务页面 import Service

4. 条件分支

Platform.operatingSystem == 'ohos'

5. 抽象架构

接口 + 工厂,让业务无感

至此,你已经掌握了鸿蒙端最核心的能力——跨端调用。下一篇我们谈另一个高频踩坑点:鸿蒙权限申请与运行时授权

下篇预告

→ 12 · 鸿蒙权限申请与运行时授权 ⭐


© 版权声明

相关文章

1 条评论

none
暂无评论...