Flutter與原生通信概述

分類簡介

flutter與原生通信主要有三種方式:MethodChannel、EventChannel、BasicMessageChannel,這三種方式均各有適用的場景:MethodChannel用于native與flutter的方法調用,EventChannel用于native單向的向flutter發(fā)送廣播消息,BasicMessageChannel用于native與flutter之間的消息互發(fā)。

詳細使用

MethodChannel

MethodChannel用于雙方之間的方法互調,使用步驟是:
1.創(chuàng)建一個MethodChannel對象,傳入MethodChannel名稱。
2.使用setMethodHandle對對方調用自己的方法進行監(jiān)聽,通過回調中的MethodCall對象方法名判斷、獲取方法參數(shù),并且返回調用結果。
3.使用invokeMethod來調用對方的方法,可傳入方法名,方法參數(shù),以及監(jiān)聽對方的回調結果。
以下是示例:

native

    public void registerWith(FlutterEngine flutterEngine) {
        methodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "MethodChannel");
//監(jiān)聽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");
    //監(jiān)聽native調用flutter方法
    methodChannel.setMethodCallHandler((methodCall) async {
      if (methodCall.method == 'nativeCallFlutterMethod') {
        String args = 'nativeCallFlutterMethod 方法被調用了,參數(shù)為${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示例對象再解析到相應的方法,而是將雙方互發(fā)的消息包裝成了MethodCall對象,拿到這個對象后通過MethodCall里面的方法名去判斷要做什么操作,并不是直接就調用了自身(native或flutter)相對應的方法。具體要做什么操作、調用什么方法還是得自己去調用和實現(xiàn)。

EventChannel

EventChannel適用于native向flutter發(fā)送廣播消息,只是單向的消息發(fā)送,native發(fā),flutter收,返過來flutter并不能向native發(fā)送消息。例如native可將定位數(shù)據(jù)不斷的報給flutter,或者錄像數(shù)據(jù)等等,所有基于原生能力產生的數(shù)據(jù)都可以通過EventChannel進行發(fā)送。
步驟:
1.創(chuàng)建一個EventChannel對象,傳入EventChannel名稱。
2.flutter端調用receiveBroadcastStream進行廣播消息注冊,傳入arguments參數(shù)即為廣播名稱,此參數(shù)是告訴native端你要接受的廣播類型,判別是什么廣播發(fā)送的數(shù)據(jù)。
2.native調用setStreamHandler方法進行廣播消息監(jiān)聽,onListen回調里會有一個arguments參數(shù),這里及為flutter注冊的廣播類型,若flutter端沒有注冊,則native端不會收到這個回調,也就無法進行消息發(fā)送。收到flutter端的廣播注冊后,根據(jù)arguments可判斷廣播類型,然后根據(jù)EventChannel.EventSink來進行消息發(fā)送,EventSink.success()即可將消息發(fā)送給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("經(jīng)緯度。。。");
                }
            }

            @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就是比較常用的消息互發(fā),使用步驟如下:
1.創(chuàng)建BasicMessageChannel對象,傳入BasicMessageChannel名稱。還需傳入編解碼方式(可以自己實現(xiàn)),系統(tǒng)提供了一些列的編解碼方式,后續(xù)會介紹到。
2.使用setMessageHandler方法進行消息監(jiān)聽,也可進行回復。
3.使用send方法進行消息發(fā)送。

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

編碼方式

無論哪種方式的消息傳遞,最終都是將自定義數(shù)據(jù)轉化為二進制數(shù)據(jù)進行傳遞,flutter提供的編解碼方式分為MethodCodec和MessageCodec兩種,EventChannel和MethodChannel使用的就是MethodCodec,BasicMessageChannel使用的是MessageCodec。MethodCodec其實就是在MessageCodec的基礎上將數(shù)據(jù)包裝了一下,使其轉化為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對象之間的互轉,后一種則是根據(jù)傳入的數(shù)據(jù)基本類型(String,Integer等)來進行互轉。
MessageCodec則提供了四種方式,如下圖,具體就不詳細講述了,看看名字就知道是怎么回事,可以直接去看源碼。最常用和默認的就是StandardMessageCodec方式。


image.png

FlutterPlugin使用方式

從上面的使用方式可以看出,每一種Channel在創(chuàng)建的時候都需要傳遞一個BinaryMessenger,這個接口可以在FlutterEngine里面拿到,因此需要在FlutterActivity里面實現(xiàn)configFlutterEngine方法里面重寫這個方法。FlutterActivity在attach FlutterEngine之后就會調用這個configFlutterEngine方法,通過flutterEngine.getPlugins().add(FlutterPlugin)方法可以FlutterPlugin的回調方法里進行數(shù)據(jù)的初始化和銷毀工作。如下圖

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;
    }
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容