阶段:阶段四 · 鸿蒙端深度适配(专栏核心) | 篇号:11 / 25 |
开篇
上一篇讲了桥接原理,这一篇动手写代码——我们要打通一个完整链路:
Dart 调用 → MethodChannel → 鸿蒙 ArkTS 原生 API → 返回结果给 Dart
并以「获取鸿蒙设备信息 + 接入华为推送」两个实战案例,把跨端调用的标准模式彻底掌握。这是后续做任何鸿蒙原生功能的基础。
本篇你将收获
- ✅ 完整跑通一次「Dart → ArkTS → Dart」调用闭环
- ✅ 学会鸿蒙常用 API(设备信息 / 推送 / 振动)的接入方式
- ✅ 掌握「条件编译」:让代码只在鸿蒙端执行
- ✅ 写出可测试、可复用的 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 · 鸿蒙权限申请与运行时授权 ⭐





