前言
在实际的开发中通常需要 Flutter 调用 Native 的功能,或者 Native 调用 Flutter 的功能
它们之间的通信主要是通过 Platform Channel 来实现的, 主要有 3 种 channel :
- MethodChannel 用于传递方法调用
- EventChannel 用于数据流(event streams)的通信
- BasicMessageChannel 用于传递字符串和半结构化的信息
下图以 MethodChannel 为例, 展示了 Flutter 和 Native 之间的消息传递:
在这里插入图片描述
为了应用的流畅度, 能够及时响应用户的操作, Flutter 和 Native 之间消息和响应的传递都是异步的, 但是调用 channel api 的时候需要在 主线程 中调用
Platform Channel 支持的数据类型
Platform Channel 通过标准的消息编解码器来为我们在 发送 和 接收 数据时自动 序列化 和 反序列化
编解码支持的数据类型有:
| Dart | Android | iOS |
|---|---|---|
| null | null | nil (NSNull when nested) |
| bool | java.lang.Boolean | NSNumber numberWithBool: |
| int | java.lang.Integer | NSNumber numberWithInt: |
| int(if 32 bits not enough) | java.lang.Long | NSNumber numberWithLong: |
| double | java.lang.Double | NSNumber numberWithDouble: |
| String | java.lang.String | NSString |
| Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
| Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
| Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
| Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
| List | java.util.ArrayList | NSArray |
| Map | java.util.HashMap | NSDictionary |
MethodChannel
以 Flutter 获取 手机电量 为例, 在 Flutter 界面中要想获取 Android/iOS 的电量, 首先要在 Native 编写获取电量的功能, 供 Flutter 来调用
Native 端代码
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.flutter_battery/battery";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
(call, result) -> {
// 在主线程中执行
if (call.method.equals("getBatteryLevel")) {
// 获取电量
int batteryLevel = fetchBatteryLevel();
if (batteryLevel != -1) {
// 将电量返回给 Flutter 调用
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
});
}
// 获取电量的方法
private int fetchBatteryLevel() {
int batteryLevel;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
}
在 Native 代码中, 我们新建了一个 fetchBatteryLevel 函数来获取电量, 然后 new 一个 MethodChannel 对象
这里需要注意该构造函数的第二个参数 CHANNEL, 这个字符串在稍后的 Flutter 中也要用到
最后为 MethodChannel 设置函数调用处理器 MethodCallHandler, 也就是 Flutter 调用 Native 函数的时候会回调这个MethodCallHandler
Flutter 端代码
class _MyHomePageState extends State<MyHomePage> {
// 构造函数参数就是上面 Android 的 CHANNEL 常量
static const methodChannelBattery = const MethodChannel('com.example.flutter_battery/battery');
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
// invokeMethod('getBatteryLevel') 会回调 MethodCallHandler
final int result = await methodChannelBattery.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
} on MissingPluginException catch (e) {
batteryLevel = "plugin undefined";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
margin: EdgeInsets.only(left: 10, top: 10),
child: Center(
child: Column(
children: [
Row(
children: <Widget>[
RaisedButton(
child: Text(
'GetBatteryFromNative',
style: TextStyle(fontSize: 12),
),
onPressed: _getBatteryLevel,
),
Padding(
padding: EdgeInsets.only(left: 10),
child: Text(_batteryLevel),
)
],
),
],
),
),
),
);
}
}
点击 Flutter 界面上的按钮就可以获取到 Android 手机里的电量了:
0
MethodChannel 除了使用实现 Flutter 调用 Native 函数, 也可以 Native 调用 Flutter 函数
首先要在 Native 端调用 invokeMethod 方法, 指定你要调用哪个 Flutter 方法:
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = fetchBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
// Native 调用 Flutter 的 getFlutterContent 函数
channel.invokeMethod("getFlutterContent", null, new MethodChannel.Result() {
@Override
public void success(Object o) {
Log.e("BatteryPlugin", "Dart getFlutterContent() result : " + o);
}
@Override
public void error(String s, String s1, Object o) {
Log.e("BatteryPlugin", "Dart getFlutterContent() error : " + s);
}
@Override
public void notImplemented() {
Log.e("BatteryPlugin", "Dart getFlutterContent() notImplemented");
}
});
}
然后在 Flutter 中设置 MethodChannel 的 MethodCallHandler, 也就是说 Native 调用了 invokeMethod 方法后, Flutter 怎么处理:
void initState() {
super.initState();
methodChannelBattery.setMethodCallHandler(batteryCallHandler);
}
Future<dynamic> batteryCallHandler(MethodCall call) async {
switch (call.method) {
case "getFlutterContent":
return "This is FlutterContent";
}
}
上面代码的主要意思是, 当我们点击按钮调用 Native 里的函数获取电量, 然后在 Native 中立马调用 Flutter 中的 getFlutterContent 函数
然后控制台就会输出, 我们从 Flutter getFlutterContent() 的返回值:
Dart getFlutterContent() result : This is FlutterContent
EventChannel
EventChannel 适用于事件流的通信, 例如 Native 需要频繁的发送消息给 Flutter, 比如监听网络状态, 蓝牙设备等等然后发送给 Flutter
下面我们以一个案例来介绍 EventChannel 的使用, 该案例是在 Native 中每秒发送一个事件给 Flutter:
Native 端代码
public class EventChannelPlugin implements EventChannel.StreamHandler {
private Handler handler;
private static final String CHANNEL = "com.example.flutter_battery/stream";
private int count = 0;
public static void registerWith(PluginRegistry.Registrar registrar) {
// 新建 EventChannel, CHANNEL常量的作用和 MethodChannel 一样的
final EventChannel channel = new EventChannel(registrar.messenger(), CHANNEL);
// 设置流的处理器(StreamHandler)
channel.setStreamHandler(new EventChannelPlugin());
}
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
// 每隔一秒数字+1
handler = new Handler(message -> {
// 然后把数字发送给 Flutter
eventSink.success(++count);
handler.sendEmptyMessageDelayed(0, 1000);
return false;
});
handler.sendEmptyMessage(0);
}
@Override
public void onCancel(Object o) {
handler.removeMessages(0);
handler = null;
count = 0;
}
}
Flutter 端代码
class _MyHomePageState extends State<MyHomePage> {
// 创建 EventChannel
static const stream = const EventChannel('com.example.flutter_battery/stream');
int _count = 0;
StreamSubscription _timerSubscription;
void _startTimer() {
if (_timerSubscription == null)
// 监听 EventChannel 流, 会触发 Native onListen回调
_timerSubscription = stream.receiveBroadcastStream().listen(_updateTimer);
}
void _stopTimer() {
_timerSubscription?.cancel();
_timerSubscription = null;
setState(() => _count = 0);
}
void _updateTimer(dynamic count) {
print("--------$count");
setState(() => _count = count);
}
@override
void dispose() {
super.dispose();
_timerSubscription?.cancel();
_timerSubscription = null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
margin: EdgeInsets.only(left: 10, top: 10),
child: Center(
child: Column(
children: [
Row(
children: <Widget>[
RaisedButton(
child: Text('Start EventChannel',
style: TextStyle(fontSize: 12)),
onPressed: _startTimer,
),
Padding(
padding: EdgeInsets.only(left: 10),
child: RaisedButton(
child: Text('Cancel EventChannel',
style: TextStyle(fontSize: 12)),
onPressed: _stopTimer,
)),
Padding(
padding: EdgeInsets.only(left: 10),
child: Text("$_count"),
)
],
)
],
),
),
),
);
}
}
效果如下图所示:
EventChannel
BasicMessageChannel
BasicMessageChannel 更像是一个消息的通信, 如果仅仅是简单的通信而不是调用某个方法或者是事件流, 可以使用 BasicMessageChannel
BasicMessageChannel 也可以实现 Flutter 和 Native 的双向通信, 下面的示例图就是官方的例子:
image
点击
Native FAB 通知 Flutter 更新, 点击 Flutter FAB 通知 Native 更新
Flutter端代码
class _MyHomePageState extends State<MyHomePage> {
static const String _channel = 'increment';
static const String _pong = 'pong';
static const String _emptyMessage = '';
static const BasicMessageChannel<String> platform =
BasicMessageChannel<String>(_channel, StringCodec());
int _counter = 0;
@override
void initState() {
super.initState();
// 设置消息处理器
platform.setMessageHandler(_handlePlatformIncrement);
}
// 如果接收到 Native 的消息 则数字+1
Future<String> _handlePlatformIncrement(String message) async {
setState(() {
_counter++;
});
// 发送一个空消息
return _emptyMessage;
}
// 点击 Flutter 中的 FAB 则发消息给 Native
void _sendFlutterIncrement() {
platform.send(_pong);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BasicMessageChannel'),
),
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Center(
child: Text(
'Platform button tapped $_counter time${_counter == 1 ? '' : 's'}.',
style: const TextStyle(fontSize: 17.0)),
),
),
Container(
padding: const EdgeInsets.only(bottom: 15.0, left: 5.0),
child: Row(
children: <Widget>[
Image.asset('assets/flutter-mark-square-64.png', scale: 1.5),
const Text('Flutter', style: TextStyle(fontSize: 30.0)),
],
),
),
],
)),
floatingActionButton: FloatingActionButton(
onPressed: _sendFlutterIncrement,
child: const Icon(Icons.add),
),
);
}
}
Native端代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 省略其他代码...
messageChannel = new BasicMessageChannel<>(flutterView, CHANNEL, StringCodec.INSTANCE);
messageChannel.
setMessageHandler(new MessageHandler<String>() {
@Override
public void onMessage(String s, Reply<String> reply) {
// 接收到Flutter消息, 更新Native
onFlutterIncrement();
reply.reply(EMPTY_MESSAGE);
}
});
FloatingActionButton fab = findViewById(R.id.button);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通知 Flutter 更新
sendAndroidIncrement();
}
});
}
private void sendAndroidIncrement() {
messageChannel.send(PING);
}
private void onFlutterIncrement() {
counter++;
TextView textView = findViewById(R.id.button_tap);
String value = "Flutter button tapped " + counter + (counter == 1 ? " time" : " times");
textView.setText(value);
}
关于
Flutter和Native之间的通信就介绍到这里了. 总而言之, 如果通信需要方法调用可以使用MethodChannel, 通信的时候用到数据流则使用EventChannel, 如果仅仅是消息通知则可以使用BasicMessageChannel.
Reference
https://flutter.dev/docs/development/platform-integration/platform-channels
https://juejin.im/post/5b84ff6a6fb9a019f47d1cc9
https://juejin.im/post/5b4c3c9a5188251ac446d915
https://juejin.im/post/5b3ae6b96fb9a024ba6e0dbb
联系我
所有关于 Retrofit 的使用案例都在我的 AndroidAll GitHub 仓库中。该仓库除了 Retrofit,还有其他 Android 其他常用的开源库源码分析,如「RxJava」「Glide」「LeakCanary」「Dagger2」「Retrofit」「OkHttp」「ButterKnife」「Router」等。除此之外,还有完整的 Android 程序员所需要的技术栈思维导图,欢迎享用。
下面是我的公众号,干货文章不错过,有需要的可以关注下,有任何问题可以联系我:
公众号: chiclaim














网友评论