Flutter 与原生通信总结

一、通信方式介绍

  • MethodChannel 与原生互相进行方法调用,用于方法调用(双向)
  • BasicMessageChannel 与原生互相发送消息,用于数据传输(双向)
  • EventChannel 原生发送消息,Flutter 接收,用于数据流通信(单向)

二、三种方式使用对比

以下主要针对 MethodChannel 这种常用通信方式做详细分析。

三、MethodChannel 通信类设计

  • 以下为 Android 端类图,对于 Dart 侧,基本一致,不细述


关于 MethodChannel 方式相互调用的流程简图如下:


  • invokeMethod() 发送消息,将 MethodCall 进行编码传输,Native 端是编码为 ByteBuffer ,Dart 侧则是编码为 ByteData
  • Native 侧收到 Dart 消息后,底层通过 FlutterJNI 实现,上层通过 BinaryMessageHandler.onMessage() 回调监听,将收到的 ByteBuffer 解码为 MethodCall ;
  • Native 根据方法传参做逻辑处理后,若要回复消息/回传返回值,则通过 BinaryReply result 进行,最终传输的数据会被编码为 ByteBuffer 回信过去,方法也是表达得极为明确:encodeEnvelope() 回信。

消息的编码与解码 MessageCodec,Dart 和 native 侧分别有四种编码方式,两端都是一致的。


备注:点击查看 类型对应关系

四、MethodChannel 方法调用示例

无论是 Dart 调用 Android,还是 Android 调用 Dart,均使用 invokeMethod()触发调用 以及 setMethodCallHandler() 监听调用事件进行。

1. Dart 调用 Native 方法

1)调用 Native 方法getMessageMethodChannel.invokeMethod('getMessage')
2)Native 监听方法调用并执行对应方法 Native 逻辑
3)如果需要回传 Native 数据给 Dart,则使用:result.success() 传递数据,该结果直接从上述 invokeMethod() 方法可获取到。

class MainActivity: FlutterActivity() {

   val CHANNEL = "native.call";

    var count = 0;

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        channel.setMethodCallHandler { call, result ->
            // Note: this method is invoked on the main thread.
            if (call.method == "showToast") {
                Toast.makeText(this, "Hello flutter, I'm Android !", Toast.LENGTH_LONG).show();
            } else if (call.method == "getMessage") {
                // 一次会话通道只能回复一次,不能回复多次,否则抛异常:java.lang.IllegalStateException: Reply already submitted
                result.success("Android: ${count++}")
            }

            Log.e("DartCall", Thread.currentThread().name)

            for (i in 0..5) {
                channel.invokeMethod("callDart", "Hell dart, $i")
            }
        }
    }
}

2. Native 调用 Dart 方法

  • 在 initState() 方法中,注册 native 调用 dart 方法监听:platform.setMethodCallHandler((MethodCall call) { }
  • 处理 native 方法传参。
  // 1. Android 调用 Dart 方法:
  for (i in 0..5) {
     channel.invokeMethod("callDart", "Hell dart, $i")
  }

  // 2. Dart 侧注册 Native 事件调用监听
  @override
  void initState() {
    super.initState();
    registerNativeCall();
  }

  dynamic nativeArgs;
  void setNativeArgs(dynamic args) {
    nativeArgs = args;
    print('$nativeArgs');
  }
  
  void registerNativeCall() {
    platform.setMethodCallHandler((MethodCall call) {
      if (call.method == 'callDart') {
        setNativeArgs(call.arguments);
      }
    });
  }

3. 注意事项

1)Dart 调用 Native 方法,Native 侧可以直接使用 MethodChannel.Result 回传数据给 Dart,但是一次通信过程中,result.success() 方法只能调用一次,否则就会出现以下异常:

E/DartMessenger(19357): Uncaught exception in binary message listener
E/DartMessenger(19357): java.lang.IllegalStateException: Reply already submitted
E/DartMessenger(19357):     at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:149)
E/DartMessenger(19357):     at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:253)
E/DartMessenger(19357):     at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:91)
E/DartMessenger(19357):     at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:724)
E/DartMessenger(19357):     at android.os.MessageQueue.nativePollOnce(Native Method)
E/DartMessenger(19357):     at android.os.MessageQueue.next(MessageQueue.java:326)
E/DartMessenger(19357):     at android.os.Looper.loop(Looper.java:160)
E/DartMessenger(19357):     at android.app.ActivityThread.main(ActivityThread.java:6898)
E/DartMessenger(19357):     at java.lang.reflect.Method.invoke(Native Method)
E/DartMessenger(19357):     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
E/DartMessenger(19357):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

解决方法,如果要持续传递 Native 数据给 Dart,则直接使用 invokeMethod() 方法,Dart 侧监听到自己方法调用,再做处理。

4. 完整示例


import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class NativeCallDemoWidget extends StatefulWidget {
  
  @override
  State createState() => NativeCallState();
  
}

class NativeCallState extends State {
  
  static const platform = MethodChannel('native.call');

  String nativeMessage = 'Hello: ';

  Future showToast() async {
    await platform.invokeMethod('showToast');
  }

  Future getMessage() async {
    String result = await platform.invokeMethod('getMessage');

    // 更新页面数据
    setState(() {
      nativeMessage += result;
    });

    return result;
  }

  dynamic nativeArgs;
  void setNativeArgs(dynamic args) {
    nativeArgs = args;
    print('$nativeArgs');
  }

  void registerNativeCall() {
    platform.setMethodCallHandler((MethodCall call) {
      if (call.method == 'callDart') {
        setNativeArgs(call.arguments);
      }
    });
  }

  @override
  void initState() {
    super.initState();
    registerNativeCall();
  }
  
  @override
  Widget build(BuildContext context) {
    Widget body = Column(
      children: [
        FlatButton(
          child: Text('Show Native Toast'),
          onPressed: () {
            showToast();
          },
          shape: RoundedRectangleBorder(
              side: BorderSide()
          ),
        ),

        FlatButton(
          child: Text('getMessage from Native'),
          onPressed: () {
            getMessage();
          },
          shape: RoundedRectangleBorder(
              side: BorderSide()
          ),
        ),

        Text(nativeMessage)
      ],
    );

    return new MaterialApp(
        title: 'Flutter 与 Native 通信方式',
        theme: new ThemeData(
            primarySwatch: Colors.blue,
            primaryColor: Colors.blue
        ),
        home: new Scaffold(
          appBar: new AppBar(
            title: new Text(
            'Flutter 与 Native 通信方式'
          ),
        ),
          body: body,
      ),
    );
  }
  
}

六、相关文档

  • 工匠若水 - Channel 源码解析:https://blog.csdn.net/yanbober/article/details/119521158?spm=1001.2014.3001.5501

你可能感兴趣的:(Flutter 与原生通信总结)