Platform Channel工作原理

Platform Channel工作原理

Flutter定義了三種不同類型的Channel,它們分別是

  • BasicMessageChannel:用于傳遞字符串和半結構化的信息。
  • MethodChannel:用于傳遞方法調用(method invocation)。
  • EventChannel: 用于數據流(event streams)的通信。

三種Channel之間互相獨立,各有用途,但它們在設計上卻非常相近。每種Channel均有三個重要成員變量:

  • name: String類型,代表Channel的名字,也是其唯一標識符。
  • messager:BinaryMessenger類型,代表消息信使,是消息的發送與接收的工具。
  • codec: MessageCodec類型或MethodCodec類型,代表消息的編解碼器。

使用方法

BasicMessageChannel

Android端:

BasicMessageChannel mBasicMessageChannel = new BasicMessageChannel(getFlutterView(), "basic_channel", StringCodec.INSTANCE);
mBasicMessageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler() {
    //接受消息
    @Override
    public void onMessage(Object o, BasicMessageChannel.Reply reply) {
        Log.e("basic_channel", "接收到來自flutter的消息:"+o.toString());
        reply.reply("回饋消息");
    }
});
//發送消息
mBasicMessageChannel.send("向flutter發送消息");
//發送消息并接受flutter的回饋
mBasicMessageChannel.send("向flutter發送消息", new BasicMessageChannel.Reply() {
            @Override
            public void reply(Object o) {

            }
});

Flutter端:

const basicMessageChannel = const BasicMessageChannel('basic_channel', StringCodec());
//接受并回復消息
basicMessageChannel.setMessageHandler(
      (String message) => Future<String>(() {
            setState(() {
              this.message = message;
            });
            return "回復native消息";
      }),
);
//發送消息
basicMessageChannel.send("來自flutter的message");
//flutter并沒有發送并接受回復消息的`send(T message, BasicMessageChannel.Reply<T> callback)`方法

MethodChannel

Android端:

MethodChannel mMethodChannel = new MethodChannel(getFlutterView(), "method_channel");
mMethodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
    //響應flutter端的調用
    @Override
    public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
        if (methodCall.method.equals("noticeNative")) {
            todo()
            result.success("接受成功");
        }
    }
});
//原生調用flutter
mMethodChannel.invokeMethod("noticeFlutter", "argument", new MethodChannel.Result() {
            @Override
            public void success(Object o) {
                //回調成功
            }
            @Override
            public void error(String s,String s1, Object o) {
                //回調失敗
            }
            @Override
            public void notImplemented() {

            }
});

Flutter端:

const methodChannel = const MethodChannel('method_channel');
Future<Null> getMessageFromNative() async {
    //flutter調原生方法
    try {
      //回調成功
      final String result = await methodChannel.invokeMethod('noticeNative');
      setState(() {
        method = result;
      });
    } on PlatformException catch (e) {
      //回調失敗
    }
  }
methodChannel.setMethodCallHandler(
      (MethodCall methodCall) => Future<String>(() {
            //響應原生的調用
          if(methodCall.method == "noticeFlutter"){
            setState(() {

            });
          }
      }),
); 

EventChannel

Android端:

EventChannel eventChannel = new EventChannel(getFlutterView(),"event_channel");
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
    @Override
    public void onListen(Object o, EventChannel.EventSink eventSink) {
        eventSink.success("成功");
        //eventSink.error("失敗","失敗","失敗");
    }
    @Override
    public void onCancel(Object o) {
        //取消監聽時調用
    }
});

Flutter端:

const eventChannel = const EventChannel('event_channel');
eventChannel.receiveBroadcastStream().listen(_onEvent,onError:_onError);
void _onEvent(Object event) {
    //返回的內容
}
void _onError(Object error) {
    //返回的回調
}

其中:Object args是傳遞的參數,EventChannel.EventSink eventSink是Native回調Dart時的會回調函數,eventSink提供success、error與endOfStream三個回調方法分別對應事件的不同狀態

源碼初探

Platform Channel基本結構

首先了解一下這三種Channel的代碼:

BasicMessageChannel
class BasicMessageChannel<T> {
  const BasicMessageChannel(this.name, this.codec);
  final String name;
  final MessageCodec<T> codec;
  Future<T> send(T message) async {
    return codec.decodeMessage(await BinaryMessages.send(name, codec.encodeMessage(message)));
  }
  void setMessageHandler(Future<T> handler(T message)) {
    if (handler == null) {
      BinaryMessages.setMessageHandler(name, null);
    } else {
      BinaryMessages.setMessageHandler(name, (ByteData message) async {
        return codec.encodeMessage(await handler(codec.decodeMessage(message)));
      });
    }
  }
  void setMockMessageHandler(Future<T> handler(T message)) {
    if (handler == null) {
      BinaryMessages.setMockMessageHandler(name, null);
    } else {
      BinaryMessages.setMockMessageHandler(name, (ByteData message) async {
        return codec.encodeMessage(await handler(codec.decodeMessage(message)));
      });
    }
  }
}

MethodChannel
class MethodChannel {
  const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);
  final String name;
  final MethodCodec codec;
  void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) {
    BinaryMessages.setMessageHandler(
      name,
      handler == null ? null : (ByteData message) => _handleAsMethodCall(message, handler),
    );
  }
  void setMockMethodCallHandler(Future<dynamic> handler(MethodCall call)) {
    BinaryMessages.setMockMessageHandler(
      name,
      handler == null ? null : (ByteData message) => _handleAsMethodCall(message, handler),
    );
  }
  Future<ByteData> _handleAsMethodCall(ByteData message, Future<dynamic> handler(MethodCall call)) async {
    final MethodCall call = codec.decodeMethodCall(message);
    try {
      return codec.encodeSuccessEnvelope(await handler(call));
    } on PlatformException catch (e) {
      returun ...
    } on MissingPluginException {
      return null;
    } catch (e) {
      return ...
    }
  }
  Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
    assert(method != null);
    final ByteData result = await BinaryMessages.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );
    if (result == null) {
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    final T typedResult = codec.decodeEnvelope(result);
    return typedResult;
  }
}

EventChannel
class EventChannel {
  const EventChannel(this.name, [this.codec = const StandardMethodCodec()]);
  final String name;
  final MethodCodec codec;
  Stream<dynamic> receiveBroadcastStream([dynamic arguments]) {
    final MethodChannel methodChannel = MethodChannel(name, codec);
    StreamController<dynamic> controller;
    controller = StreamController<dynamic>.broadcast(onListen: () async {
      BinaryMessages.setMessageHandler(name, (ByteData reply) async {
        ...
      });
      try {
        await methodChannel.invokeMethod<void>('listen', arguments);
      } catch (exception, stack) {
        ...
      }
    }, onCancel: () async {
      BinaryMessages.setMessageHandler(name, null);
      try {
        await methodChannel.invokeMethod<void>('cancel', arguments);
      } catch (exception, stack) {
        ...
      }
    });
    return controller.stream;
  }
}

這三種Channel都有兩個成員變量:

  • name:表示Channel名字,用于區分不同Platform Channel的唯一標志,每個Channel使用唯一的name作為其唯一標志
  • codec: 表示消息的編解碼器,Flutter采用了二進制字節流作為數據傳輸協議:發送方需要把數據編碼成二進制數據,接受方再把數據解碼成原始數據.而負責編解碼操作的就是Codec。 每個Channel中都使用到了BinaryMessages,它起到了信使的作用,負責將信息進行跨平臺的搬運,是消息發送和接受的工具。
setMessageHandler

在創建好BasicMessageChannel后,讓其接受來自另一平臺的消息,BinaryMessenger調用它的setMessageHandler方法為其設置一個消息處理器,配合BinaryMessenger完成消息的處理以及回復;

send

在創建好BasicMessageChannel后,可以調用它的send方法向另一個平臺傳遞數據。

setMethodCallHandler

設置用于在此MethodChannel上接收方法調用的回調

receiveBroadcastStream

設置廣播流以接收此EventChannel上的事件

Handler

Flutter使用Handler處理Codec解碼后的消息。三種Platform Channel相對應,Flutter中也定義了三種Handler:

  • MessageHandler: 用于處理字符串或者半結構化消息,定義在BasicMessageChannel中.
  • MethodCallHandler: 用于處理方法調用,定義在MethodChannel中.
  • StreamHandler: 用于事件流通信,定義在EventChannel中

使用Platform Channel時,需要為其注冊一個對應BinaryMessageHandler為其設置對應的Handler。二進制數據會被BinaryMessageHanler進行處理,首先使用Codec進行解碼操作,然后再分發給具體Handler進行處理。

最后

在Flutter與Native混合開發的模式下,Platform Channel的應用場景非常多,理解Platform Channel的工作原理,有助于我們在從事這方面開發時能做到得心應手。
實戰混合式開發Flutter3.0手冊
跟完MethodChannel的源碼,會發現整個通信機制還挺簡單的,先去不去理解Codec的話,等于就是將dart的變量,傳到dart Native,然后交到java Native, 再傳到java。然后相反的路徑,再從java到dart。

然后再去看BasicMessageChannel就是沒有MethodCall這個結構的,其他的也是走的BinaryMessages.send方法。然后在Android端,沒有=IncomingMethodCallHandler這個類,直接就是BinaryMessageHandler。所以了解了MethodChannelBasicMessageChannel原理自然就懂了。

同樣的EventChannel則是基于MethodChannel來實現的,只是兩端的handler會有一些特殊的處理方式。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373