Flutter 如何和 Native 通信 -Android 视角,首发 10 万字 Android 开发实战文档
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);}
return batteryLevel;}
这个函数需要能被 Flutter app 调用,此时就需要通过MethodChannel
来建立这个通道了。 首先在MainActivity
的onCreate
函数中加入以下代码来新建一个MethodChannel
public class MainActivity extends FlutterActivity {//channel 的名称,由于 app 中可能会有多个 channel,这个名称需要在 app 内是唯一的。private static final String CHANNEL = "samples.flutter.io/battery";
@Overridepublic void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);GeneratedPluginRegistrant.registerWith(this);
// 直接 new MethodChannel,然后设置一个 Callback 来处理 Flutter 端调用 new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(new MethodCallHandler() {@Overridepublic void onMethodCall(MethodCall call, Result result) {// 在这个回调里处理从 Flutter 来的调用}});}}
注意,每个
MethodChannel
需要有唯一的字符串作为标识,用以互相区分,这个名称建议使用package.module...
这样的模式来命名。因为所有的MethodChannel
都是保存在以通道名为 Key 的 Map 中。所以你要是设了两个名字一样的 channel,只有后设置的那个会生效。
接下来我们来填充onMethodCall
。
@Overridepublic void onMethodCall(MethodCall call, Result 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();}}
onMethodCall
有两个入参,MethodCall
里包含要调用的方法名称和参数。Result
是给 Flutter 的返回值。方法名是两端协商好的。通过 if 语句判断MethodCall.method
来区分不同的方法,在我们的例子里面我们只会处理名为“getBatteryLevel”的调用。在调用本地方法获取到电量以后通过result.success(batteryLevel)
调用把电量值返回给 Flutter。 Native 端的代码就完成了。是不是很简单?
MethodChannel-Flutter 端
接下来看 Flutter 端代码怎么写: 首先在 State
中创建 Flutter 端的MethodChannel
import 'dart:async';
import 'package:flutter/material.dart';import 'package:flutter/services.dart';...class _MyHomePageState extends State<MyHomePage> {static const platform = const MethodChannel('samples.flutter.io/battery');
// Get battery level.}
channel 的名称要和 Native 端的一致。 然后是通过 MethodChannel 调用的代码
String _batteryLevel = 'Unknown battery level.';
Future<Null> _getBatteryLevel() async {String batteryLevel;try {final int result = await platform.invokeMethod('getBatteryLevel');batteryLevel = 'Battery level at {e.message}'.";}
setState(() {_batteryLevel = batteryLevel;});}
final int result = await platform.invokeMethod('getBatteryLevel');
这行代码就是通
过通道来调用 Native 方法了。注意这里的await
关键字。前面我们说过 MethodChannel 是异步的,所以这里必须要使用await
关键字。 在上面 Native 代码中我们把获取到的电量通过result.success(batteryLevel);
返回给 Flutter。这里await
表达式执行完成以后电量就直接赋值给result
变量了。剩下的就是怎么展示的问题了,就不再细说了,具体可以去看代码。
需要注意的是,这里我们只介绍了从 Flutter 调用 Native 方法,其实通过MethodChannel
,Native 也能调用 Flutter 的方法,这是一个双向的通道。
举个例子,我们想从 Native 端请求 Flutter 端的一个getName
方法获取一个字符串。在 Flutter 端你需要给MethodChannel
设置一个MethodCallHandler
_channel.setMethodCallHandler(platformCallHandler);
Future<dynamic> platformCallHandler(MethodCall call) async {switch (call.method) {case "getName":return "Hello from Flutter";break;}}
在 Native 端,只需要让对应的的 channel 调用invokeMethod
就行了
channel.invokeMethod("getName", null, new MethodChannel.Result() {@Overridepublic void success(Object o) {// 这里就会输出 "Hello from Flutter"Log.i("debug", o.toString());}@Overridepublic void error(String s, String s1, Object o) {}@Overridepublic void notImplemented() {}});
至此,MethodChannel
的用法就介绍完了。可以发现,通过MethodChannel
Native 和 Flutter 方法互相调用还是蛮直接的。这里只是做了个大概的介绍,具体细节和一些复杂用法还有待大家的探索。
MethodChannel
提供了方法调用的通道,那如果 Native 有数据流需要传送给 Flutter 该怎么办呢?这时候就要用到EventChannel
了。
EventChannel
EventChannel
的使用我们也以官方获取电池电量的 demo 为例,手机的电池状态是不停变化的。我们要把这样的电池状态变化由 Native 及时通过EventChannel
来告诉 Flutter。这种情况用之前讲的MethodChannel
办法是不行的,这意味着 Flutter 需要用轮询的方式不停调用getBatteryLevel
来获取当前电量,显然是不正确的做法。而用EventChannel
的方式,则是将当前电池状态"推送"给 Flutter.
EventChannel - Native 端
先看我们熟悉的 Native 端怎么来创建EventChannel
, 还是在MainActivity.onCreate
中,我们加入如下代码:
new EventChannel(getFlutterView(), "samples.flutter.io/charging").setStreamHandler(new StreamHandler() {// 接收电池广播的 BroadcastReceiver。private BroadcastReceiver chargingStateChangeReceiver;@Override// 这个 onListen 是 Flutter 端开始监听这个 channel 时的回调,第二个参数 EventSink 是用来传数据的载体。public void onListen(Object arguments, EventSink events) {chargingStateChangeReceiver = createChargingStateChangeReceiver(events);registerReceiver(chargingStateChangeReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));}
@Overridepublic void onCancel(Object arguments) {// 对面不再接收 unregisterReceiver(chargingStateChangeReceiver);chargingStateChangeReceiver = null;}});
评论