美文网首页
(八)Flutter 和 Native 之间的通信详解

(八)Flutter 和 Native 之间的通信详解

作者: Chiclaim | 来源:发表于2019-07-29 09:43 被阅读0次

    前言

    在实际的开发中通常需要 Flutter 调用 Native 的功能,或者 Native 调用 Flutter 的功能

    它们之间的通信主要是通过 Platform Channel 来实现的, 主要有 3channel :

    • MethodChannel 用于传递方法调用
    • EventChannel 用于数据流(event streams)的通信
    • BasicMessageChannel 用于传递字符串和半结构化的信息

    下图以 MethodChannel 为例, 展示了 FlutterNative 之间的消息传递:

    在这里插入图片描述

    为了应用的流畅度, 能够及时响应用户的操作, FlutterNative 之间消息和响应的传递都是异步的, 但是调用 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 中设置 MethodChannelMethodCallHandler, 也就是说 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 也可以实现 FlutterNative 的双向通信, 下面的示例图就是官方的例子:

    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);
    }
    

    关于 FlutterNative 之间的通信就介绍到这里了. 总而言之, 如果通信需要方法调用可以使用 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

    相关文章

      网友评论

          本文标题:(八)Flutter 和 Native 之间的通信详解

          本文链接:https://www.haomeiwen.com/subject/owsvrctx.html