3. MethodChannel基础:从创建到原生通信
MethodChannel,说白了就是Flutter和原生代码之间的「电话线」。
我刚开始接触Flutter插件开发时,总觉得这玩意儿很神秘。后来做多了才发现,它本质上就是一个消息通道——Flutter端发个消息过去,原生端处理完再回传结果。就这么简单。
3.1 MethodChannel的创建与注册
先看Flutter端。创建MethodChannel只需要两样东西:一个名字,一个BinaryMessenger。
// Flutter端
import 'package:flutter/services.dart';
class BatteryPlugin {
static const _channel = MethodChannel('com.example.battery');
Future<int> getBatteryLevel() async {
final result = await _channel.invokeMethod('getBatteryLevel');
return result;
}
}
这里有个坑,我踩过好几次——通道名字必须唯一。你想想看,如果两个插件用了同一个名字,消息就会串线。我个人习惯用「包名+功能名」的格式,比如 com.example.battery。
3.2 Android端方法实现
Android端要做的,就是实现 MethodChannel.MethodCallHandler 接口。
// Android端 - MainActivity.kt
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.example.battery"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL
).setMethodCallHandler { call, result ->
when (call.method) {
"getBatteryLevel" -> {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available", null)
}
}
else -> result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
}
}
嗯,这里要注意几个点:
- result.success() 返回成功结果
- result.error() 返回错误信息
- result.notImplemented() 方法未实现
我在项目中遇到过一个问题:如果原生端忘记调用 result 的任何一个方法,Flutter端就会一直等待,直到超时。所以记得每个分支都要处理 result。
3.3 iOS端方法实现
iOS端用Swift实现,逻辑和Android差不多。
// iOS端 - AppDelegate.swift
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(
name: "com.example.battery",
binaryMessenger: controller.binaryMessenger
)
batteryChannel.setMethodCallHandler { [weak self] (call, result) in
guard call.method == "getBatteryLevel" else {
result(FlutterMethodNotImplemented)
return
}
let batteryLevel = self?.getBatteryLevel() ?? -1
if batteryLevel >= 0 {
result(batteryLevel)
} else {
result(FlutterError(
code: "UNAVAILABLE",
message: "Battery level not available",
details: nil
))
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func getBatteryLevel() -> Int {
UIDevice.current.isBatteryMonitoringEnabled = true
let level = Int(UIDevice.current.batteryLevel * 100)
return level
}
}
[weak self] 避免循环引用。我刚开始写Swift时没注意这个,结果内存泄漏了。
3.4 数据类型的自动映射
MethodChannel最方便的地方,就是它会自动帮你做类型转换。Flutter的 int 到Android的 Integer,Flutter的 String 到iOS的 NSString——这些都不用你操心。
| Flutter类型 | Android类型 | iOS类型 |
|---|---|---|
| null | null | nil |
| bool | Boolean | NSNumber(bool) |
| int | Integer | NSNumber(int) |
| double | Double | NSNumber(double) |
| String | String | NSString |
| List | ArrayList | NSArray |
| Map | HashMap | NSDictionary |
为什么会自动映射?因为MethodChannel底层用的是 StandardMessageCodec,它定义了一套标准的数据编码格式。你传什么类型,它自动给你转成对应的原生类型。
举个例子,如果你想传一个用户信息:
// Flutter端
final user = {
'name': '张三',
'age': 25,
'isVip': true
};
await _channel.invokeMethod('saveUser', user);
// Android端接收时,自动转成了HashMap
val userMap = call.arguments as HashMap<String, Any>
val name = userMap["name"] as String
说白了,MethodChannel就是一套「约定大于配置」的通信机制。你只要遵守它的规则,数据就能在Flutter和原生之间自由穿梭。
我个人建议,刚开始做插件开发时,先用简单的数据类型(int、String、Map)练手。等熟悉了这套机制,再考虑更复杂的场景。
好了,MethodChannel的基础就这些。下一章我们会讲EventChannel,那是另一种通信方式——适合流式数据,比如传感器数据、位置更新等。