第三章 MethodChannel详解:Android端实战
好,咱们接着聊MethodChannel。上一章我们把Flutter端的通道搭好了,这一章轮到Android端上场了。说白了,MethodChannel就像一根电话线,Flutter那边拨号,Android这边接听,然后告诉对方结果。
我个人习惯先把通道注册好,再写业务逻辑。这样思路清晰,不容易乱。你想想看,如果代码写了一半才发现通道没注册,那得多尴尬。
3.1 创建MethodChannel(Android端)
在Android端,我们需要在MainActivity或者FlutterActivity里做这件事。嗯,这里要注意:通道的名字必须和Flutter端完全一致,大小写、拼写都不能错。我在项目中遇到过好几次,因为名字少了个下划线,结果调了半天才发现。
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.app/channel";
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
// 这里处理Flutter发来的请求
}
);
}
}
这段代码看着简单,但有几个坑我得提醒你:
- configureFlutterEngine 这个方法在Flutter引擎初始化时调用,时机刚刚好
- BinaryMessenger 必须从flutterEngine获取,不能自己new一个
- 通道名建议用常量定义,别到处写死字符串
3.2 处理Flutter端的方法调用
接下来就是重头戏了——处理Flutter发来的方法调用。MethodCallHandler接口里只有一个方法:onMethodCall。它会收到两个参数:call(方法调用信息)和result(结果回调)。
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler((call, result) -> {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
});
这里有几个关键点:
- call.method 就是Flutter端传过来的方法名,用字符串匹配
- call.arguments 可以拿到参数,可以是基本类型、Map、List等
- result.success() 返回成功结果
- result.error() 返回错误信息
- result.notImplemented() 告诉Flutter这个方法没实现
3.3 返回结果给Flutter
返回结果这块,说白了就是三种情况:成功、失败、没实现。咱们一个一个说。
成功返回:
result.success("操作成功");
result.success(42);
result.success(Arrays.asList("a", "b", "c"));
可以返回任何可序列化的类型。我在项目中遇到过一个问题:返回了一个自定义对象,结果Flutter那边解析不了。后来发现,必须用Map或者基本类型才行。
错误返回:
result.error("ERROR_CODE", "错误描述", details);
三个参数:错误码(字符串)、错误描述(字符串)、详细信息(任意类型)。错误码建议用大写字母加下划线,比如"NETWORK_ERROR"、"PERMISSION_DENIED"。
没实现:
result.notImplemented();
这个最简单,告诉Flutter端:你调的方法我没写。Flutter那边会收到MissingPluginException。
3.4 错误处理与异常传递
错误处理这块,我踩过不少坑。最典型的就是:Android端抛了个异常,Flutter端直接崩溃了。为什么会这样?因为异常没有正确传递。
正确的做法是:
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler((call, result) -> {
try {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
result.success(batteryLevel);
} else {
result.notImplemented();
}
} catch (Exception e) {
// 捕获所有异常,返回给Flutter
result.error("INTERNAL_ERROR", e.getMessage(), null);
}
});
我曾经在一个项目里忘了加try-catch,结果用户手机上的某个ROM有bug,getBatteryLevel方法抛了个RuntimeException。Flutter端直接白屏了,用户反馈说"一打开就闪退"。嗯,从那以后我再也不敢省略异常处理了。
另外,错误码的设计也很重要。我建议:
| 错误码 | 含义 | 使用场景 |
|---|---|---|
| UNAVAILABLE | 功能不可用 | 硬件不支持、权限未授予 |
| INVALID_ARGUMENTS | 参数错误 | 传入了空值、类型不对 |
| INTERNAL_ERROR | 内部错误 | 系统异常、未知错误 |
| NOT_IMPLEMENTED | 未实现 | 方法不存在 |
最后说一个细节:result对象只能调用一次。你不能先调success再调error,也不能调两次success。如果重复调用,Flutter端会收到异常。我在代码审查时经常看到这种问题,新手容易犯。
好了,Android端的MethodChannel就这些内容。下一章我们聊聊iOS端,思路差不多,但细节上有些差异。到时候我会重点说说Swift和Objective-C两种语言下的实现区别。