Flutter与原生通信概述

分类简介

flutter与原生通信主要有三种方式:MethodChannel、EventChannel、BasicMessageChannel,这三种方式均各有适用的场景:MethodChannel用于native与flutter的方法调用,EventChannel用于native单向的向flutter发送广播消息,BasicMessageChannel用于native与flutter之间的消息互发。

详细使用

MethodChannel

MethodChannel用于双方之间的方法互调,使用步骤是:
1.创建一个MethodChannel对象,传入MethodChannel名称。
2.使用setMethodHandle对对方调用自己的方法进行监听,通过回调中的MethodCall对象方法名判断、获取方法参数,并且返回调用结果。
3.使用invokeMethod来调用对方的方法,可传入方法名,方法参数,以及监听对方的回调结果。
以下是示例:

native

    public void registerWith(FlutterEngine flutterEngine) {
        methodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "MethodChannel");
//监听flutter调用
        methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
                if (call.method.equals("flutterCallNative")) {
                    result.success("args =" + call.arguments + ",result = 1");
                }
            }
        });
//调用flutter方法
        methodChannel.invokeMethod("methodName", "arguments", new MethodChannel.Result() {
            @Override
            public void success(@Nullable Object result) {

            }

            @Override
            public void error(@NonNull String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {

            }

            @Override
            public void notImplemented() {

            }
        });
    }

flutter

  Future<void> testMethodCall() async {
    var methodChannel = const MethodChannel("MethodChannel");
    //监听native调用flutter方法
    methodChannel.setMethodCallHandler((methodCall) async {
      if (methodCall.method == 'nativeCallFlutterMethod') {
        String args = 'nativeCallFlutterMethod 方法被调用了,参数为${methodCall
            .arguments}';
        Fluttertoast.showToast(msg: '$args flutterResult');
        return '$args flutterResult';
      }
    });
    //调用native方法
    String result = await methodChannel.invokeMethod("flutterCallNative", 222);
    Fluttertoast.showToast(msg: "native方法返回值:$result");
  }

需要注意的是,MethodChannel的名称需要双方保持一致,否则就不是同一个MethodChannel了。另外这里的方法调用并不是像Java里面反射那样去先找到class示例对象再解析到相应的方法,而是将双方互发的消息包装成了MethodCall对象,拿到这个对象后通过MethodCall里面的方法名去判断要做什么操作,并不是直接就调用了自身(native或flutter)相对应的方法。具体要做什么操作、调用什么方法还是得自己去调用和实现。

EventChannel

EventChannel适用于native向flutter发送广播消息,只是单向的消息发送,native发,flutter收,返过来flutter并不能向native发送消息。例如native可将定位数据不断的报给flutter,或者录像数据等等,所有基于原生能力产生的数据都可以通过EventChannel进行发送。
步骤:
1.创建一个EventChannel对象,传入EventChannel名称。
2.flutter端调用receiveBroadcastStream进行广播消息注册,传入arguments参数即为广播名称,此参数是告诉native端你要接受的广播类型,判别是什么广播发送的数据。
2.native调用setStreamHandler方法进行广播消息监听,onListen回调里会有一个arguments参数,这里及为flutter注册的广播类型,若flutter端没有注册,则native端不会收到这个回调,也就无法进行消息发送。收到flutter端的广播注册后,根据arguments可判断广播类型,然后根据EventChannel.EventSink来进行消息发送,EventSink.success()即可将消息发送给flutter端。
3.flutter进行广播注册会返回一个streamSubscription类型的对象,该对象可以进行消息的停止,native可在onCancel回调里面收到。
示例如下:

native

   public void registerWith(FlutterEngine engine) {
        EventChannel eventChannel = new EventChannel(engine.getDartExecutor().getBinaryMessenger(), "eventChannel");
        eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
            @Override
            public void onListen(Object arguments, EventChannel.EventSink events) {
                if (arguments.equals("Locations")) {
                    events.success("");
                    EventChannelPlugin.this.events = events;
                    events.success("经纬度。。。");
                }
            }

            @Override
            public void onCancel(Object arguments) {

            }
        });
    }

flutter

  Future<void> testEventChannel() async {
    var eventChannel = const EventChannel("eventChannel");
    var streamSubscription = eventChannel
        .receiveBroadcastStream("Locations")
        .listen((event) {

    }, onError: (dynamic error) {

    }, cancelOnError: true);
    streamSubscription.cancel();
  }

BasicMessageChannel

BasicMessageChannel就是比较常用的消息互发,使用步骤如下:
1.创建BasicMessageChannel对象,传入BasicMessageChannel名称。还需传入编解码方式(可以自己实现),系统提供了一些列的编解码方式,后续会介绍到。
2.使用setMessageHandler方法进行消息监听,也可进行回复。
3.使用send方法进行消息发送。

native

   public void registerWith(FlutterEngine flutterEngine) {
        BasicMessageChannel basicMessageChannel = new BasicMessageChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "BasicMessageChannel", new MessageCodec() {
            @Override
            public ByteBuffer encodeMessage(@Nullable Object message) {
                return null;
            }

            @Override
            public Object decodeMessage(@Nullable ByteBuffer message) {
                return null;
            }
        });
        basicMessageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler() {
            @Override
            public void onMessage(@Nullable Object message, @NonNull BasicMessageChannel.Reply reply) {

            }
        });
        basicMessageChannel.send("", new BasicMessageChannel.Reply() {
            @Override
            public void reply(@Nullable Object reply) {

            }
        });
    }

flutter

  Future<void> testBasicMsgChannel() async {
    var basicMessageChannel = const BasicMessageChannel(
        "BasicMessageChannel", StandardMessageCodec());
    basicMessageChannel.setMessageHandler((message) async {
      return null;
    });
    Object? test = await basicMessageChannel.send("");
  }

编码方式

无论哪种方式的消息传递,最终都是将自定义数据转化为二进制数据进行传递,flutter提供的编解码方式分为MethodCodec和MessageCodec两种,EventChannel和MethodChannel使用的就是MethodCodec,BasicMessageChannel使用的是MessageCodec。MethodCodec其实就是在MessageCodec的基础上将数据包装了一下,使其转化为MethodCall对象方便使用。
MethodCodec源码:

public interface MethodCodec {
  /**
   * Encodes a message call into binary.
   * @param methodCall a {@link MethodCall}.
   * @return a {@link ByteBuffer} containing the encoding between position 0 and the current position.
   */
  @NonNull
  ByteBuffer encodeMethodCall(@NonNull MethodCall methodCall);

  /**
   * Decodes a message call from binary.
   * @param methodCall the binary encoding of the method call as a {@link ByteBuffer}.
   * @return a {@link MethodCall} representation of the bytes between the given buffer's current
   *     position and its limit.
   */
  @NonNull
  MethodCall decodeMethodCall(@NonNull ByteBuffer methodCall);
}

MethodCodec提供了两种方式:JSONMethodCodec和StandardMethodCodec,前一种就是JSON和MethodCall对象之间的互转,后一种则是根据传入的数据基本类型(String,Integer等)来进行互转。
MessageCodec则提供了四种方式,如下图,具体就不详细讲述了,看看名字就知道是怎么回事,可以直接去看源码。最常用和默认的就是StandardMessageCodec方式。


image.png

FlutterPlugin使用方式

从上面的使用方式可以看出,每一种Channel在创建的时候都需要传递一个BinaryMessenger,这个接口可以在FlutterEngine里面拿到,因此需要在FlutterActivity里面实现configFlutterEngine方法里面重写这个方法。FlutterActivity在attach FlutterEngine之后就会调用这个configFlutterEngine方法,通过flutterEngine.getPlugins().add(FlutterPlugin)方法可以FlutterPlugin的回调方法里进行数据的初始化和销毁工作。如下图

public class MainActivity extends FlutterActivity {
    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        flutterEngine.getPlugins().add(new FlutterPlugin() {
            @Override
            public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
                new MethodChannelPlugin().registerWith(binding.getBinaryMessenger());
            }

            @Override
            public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
                
            }
        });
    }
}

这个回调方法里的FlutterPluginBinding提供了一些我们可能会用到的对象,如下:

    public FlutterPluginBinding(
        @NonNull Context applicationContext,
        @NonNull FlutterEngine flutterEngine,
        @NonNull BinaryMessenger binaryMessenger,
        @NonNull TextureRegistry textureRegistry,
        @NonNull PlatformViewRegistry platformViewRegistry,
        @NonNull FlutterAssets flutterAssets) {
      this.applicationContext = applicationContext;
      this.flutterEngine = flutterEngine;
      this.binaryMessenger = binaryMessenger;
      this.textureRegistry = textureRegistry;
      this.platformViewRegistry = platformViewRegistry;
      this.flutterAssets = flutterAssets;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容