Administrator
发布于 2026-08-25 / 21 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 54 篇:Flutter 与 Android 互操作:Platform Channel 实战

上一篇讲了热修复,这篇切换到跨平台——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 类型

类型

方向

用法

MethodChannel

双向、方法调用

异步操作(API 调用)

EventChannel

原生 → Dart 持续流

数据流(传感器、位置更新)

BasicMessageChannel

双向

自定义协议、低频消息

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 序列化,两边需要类型对应:

Dart

Kotlin

Java

null

null

null

bool

Boolean

boolean

int

Int / Long

int / long

double

Double

double

String

String

String

Uint8List

ByteArray

byte[]

List

List

ArrayList

Map

HashMap

HashMap

不识别的对象类型(比如自定义类)要走 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 的。


评论