上一篇讲了热修复,这篇切换到跨平台——Flutter 如何调用 Android 原生能力。Flutter 的 UI 用 Dart 写,但 Android 平台能力(相机、蓝牙、分享、传感器)还是要靠原生。Platform Channel 就是桥接它们的双向通道。
为什么需要 Platform Channel
Flutter 的 widget 体系是独立渲染的(Skia 引擎),不直接调用 Android 的 View 系统。但 Android 系统能力(位置、文件、网络状态)只能通过 Java/Kotlin API 访问。Platform Channel 就是 Dart ↔ 原生的通信桥梁:
Dart 代码
↓ MethodChannel.invokeMethod('getLocation')
↓
二进制序列化 (StandardMethodCodec)
↓
Android 原生 Handler 处理
↓
返回结果给 Dart
三种 Channel 类型
90% 场景用 MethodChannel。
最小示例:电池电量
Dart 端:
// lib/battery.dart
import 'package:flutter/services.dart';
class Battery {
static const _channel = MethodChannel('com.example/battery');
static Future<int> getLevel() async {
// 调用原生方法 getBatteryLevel
final int level = await _channel.invokeMethod('getBatteryLevel');
return level;
}
}
// 在 Widget 中用
class BatteryPage extends StatelessWidget {
Widget build(BuildContext context) {
return FutureBuilder<int>(
future: Battery.getLevel(),
builder: (ctx, snap) {
if (!snap.hasData) return CircularProgressIndicator();
return Text('电量: ${snap.data}%');
},
);
}
}
Android 端:
// MainActivity.kt
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.example/battery"
).setMethodCallHandler { call, result ->
when (call.method) {
"getBatteryLevel" -> {
val level = getBatteryLevel()
result.success(level)
}
else -> result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val bm = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatus: Intent? = registerReceiver(null, intent)
batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
}
return batteryLevel
}
}
就这三步:定义 channel 名 → Dart 端 invoke → 原生 handle。
数据类型映射
Channel 用 StandardMessageCodec 序列化,两边需要类型对应:
不识别的对象类型(比如自定义类)要走 Pigeon 或 protobuf 转换。
EventChannel:传感器数据流
Dart 接收持续数据流:
// lib/sensor.dart
import 'package:flutter/services.dart';
class SensorStream {
static const _channel = EventChannel('com.example/sensor');
static Stream<double> get accelerometer => _channel
.receiveBroadcastStream()
.map((event) => event as double);
}
// 使用
SensorStream.accelerometer.listen((g) {
print('加速度: $g');
});
Android 端:
// MainActivity.kt
EventChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.example/sensor"
).setStreamHandler(object : EventChannel.StreamHandler {
private var listener: SensorEventListener? = null
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
listener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
val magnitude = sqrt(
event.values[0] * event.values[0] +
event.values[1] * event.values[1] +
event.values[2] * event.values[2]
)
events.success(magnitude.toDouble())
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
}
override fun onCancel(arguments: Any?) {
listener?.let { sensorManager.unregisterListener(it) }
listener = null
}
})
onListen 是开始传数据,onCancel 是 Dart 端取消订阅。注意:每次重新订阅都会走一次 onListen,要清理资源。
Pigeon:类型安全的 Channel 通信
手写 MethodCall 容易出错,Google 官方的 Pigeon 让你用接口定义共享代码。
// pigeons/messages.dart
import 'package:pigeon/pigeon.dart';
@HostApi()
abstract class BatteryApi {
int getBatteryLevel();
}
dart run pigeon --input pigeons/messages.dart
会生成两份代码:一份 Dart,一份 Kotlin。运行时调用:
final api = BatteryApi(); // 自动生成
final level = await api.getBatteryLevel();
// Android 端
class BatteryApiImpl : BatteryApi {
override fun getBatteryLevel(): Long {
// 实现
}
}
// 注册
Pigeon.BatteryApi.setup(messenger, BatteryApiImpl())
Pigeon 适合多端共享、有复杂参数的场景。
常见坑
线程问题:原生端的 setMethodCallHandler 默认在主线程(Platform Channel 默认使用 platform 主线程)。耗时操作要切线程。
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
MethodChannel(...)
.setMethodCallHandler { call, result ->
// 这个回调默认在 UI 线程
Thread {
val data = heavyWork()
runOnUiThread { result.success(data) }
}.start()
}
}
Channel 名规范:推荐用 域名/功能 格式,比如 com.example.battery。多模块时要避免冲突。
Memory leak:setMethodCallHandler 的 handler 必须有 root reference,Context 不要传 Activity,传 ApplicationContext 或者 FlutterEngine。
嵌套 Isolate:在 Flutter 后台 isolate 里 use MethodChannel 会失败,因为 isolate 和原生通信需要不同的 binaryMessenger。
何时该写 Platform Channel
✅ 调用 Android 系统能力(Sensor、Location、Camera2)
✅ 调用已有的 Java/Kotlin SDK(推送、统计、支付的原生 SDK)
✅ 访问第三方厂商能力(华为 Push、小米推送)
❌ 自己写的逻辑(直接用 Dart 实现)
❌ UI 行为(用 widget)
❌ 网络请求(用 dio/http,不用绕到原生)
性能优化
Channel 调用是异步的,但每次调用都要序列化/反序列化。批量数据不要频繁调用:
// 错误:每帧调用一次
for (final item in items) {
await channel.invokeMethod('process', item);
}
// 正确:批量调用
await channel.invokeMethod('process', {'items': items});
小结
Platform Channel 是 Flutter 工程师的必修课。掌握 MethodChannel、EventChannel、Pigeon 三件套,基本上能完成所有 Android 互操作。理解线程模型、内存管理、序列化机制,写出稳定的双端逻辑。
下一篇进入更底层的 Android 系统服务——WMS、AMS 是怎么管理窗口和 Activity 的。