Flutter:关于EventChannel通信拿不到EventSink的思考

在项目开发中用到了Native+Flutter[Module]的方式进行开发;遇到一个场景,需要Native发消息给Flutter,于是决定采用EventChannel;
按照一些教程操作后,发现无效,在Native端始终拿不到EventSink,而且在Flutter中注册监听的地方也会报错(不过不影响其他的程序运行,只是监听的地方报错说listen有问题)。
这里先说一下我们的场景,比较骚气:页面跳转方式大概为Native->Flutter->Native,需要在后一个Native发消息给前一个Flutter;
这就很烦恼了,不管怎么操作都拿不到EventSink。
同事说用FlutterBoost吧,里面有个channel()方法都封装好了,直接用;使用后,确实可以,程序正常运行。
但是我之前的用法怎么不行?直到我看到一段话:

StreamHandler的工作原理并不复杂。当注册了一个StreamHandler后,实际上会注册一个对应的BinaryMessageHandler到BinaryMessager。而当Flutter上层开始监听事件时,会发送一个二进制消息到原生层。原生层端用MethodCodec将该消息解码为MethodCall,如果MethodCall的method值为listen,则调用StreamHandler的onListen方法,向StreamHandler传递一个EventSink。而通过EventSink向上层发送消息时,实际上就是通过BinaryMessager的send方法将消息传递过去。

通信流程也就是,得先注册,然后Handler那边留了一个回调,回调的作用就是获取EventSink;然后Flutter开始监听,对应着上面所述,当设置监听方法时,Flutter会发一个消息到Native,原生层经过解析最终会回调之前留着的那个Handler的回调,拿到EventSink,然后就可以用EventSink向Flutter发消息了。

但是我上面的做法就是先监听,Flutter往Native那边发消息时,此时Native还啥都没有;等到了Native界面时,Flutter已经调用过监听方法了,不会再次调用了,所以Native始终拿不到EventSink。

以上属于我个人的猜测,我也没有再次去验证;于是我就像看看FlutterBoost中的channel()是咋实现的,发现他不是EventChannel,而是使用的MethodChannel实现的;
源码不多,我就都贴过来

/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2019 Alibaba Group
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import 'dart:async';
import 'dart:ui';
import 'package:flutter/services.dart';

typedef Future<dynamic> EventListener(String name, Map arguments);
typedef Future<dynamic> MethodHandler(MethodCall call);

class BoostChannel {
  final MethodChannel _methodChannel = MethodChannel("flutter_boost");

  final Map<String, List<EventListener>> _eventListeners = Map();
  final Set<MethodHandler> _methodHandlers = Set();

  BoostChannel() {
    _methodChannel.setMethodCallHandler((MethodCall call) {
      if (call.method == "__event__") {
        String name = call.arguments["name"];
        Map arg = call.arguments["arguments"];
        List<EventListener> list = _eventListeners[name];
        if (list != null) {
          for (EventListener l in list) {
            l(name, arg);
          }
        }
      } else {
        for (MethodHandler handler in _methodHandlers) {
          handler(call);
        }
      }

      return Future<dynamic>.value();
    });
  }

  void sendEvent(String name, Map arguments) {
    if (name == null) {
      return;
    }

    if (arguments == null) {
      arguments = Map<dynamic, dynamic>();
    }

    Map msg = Map<dynamic, dynamic>();
    msg["name"] = name;
    msg["arguments"] = arguments;
    _methodChannel.invokeMethod<dynamic>("__event__", msg);
  }

  Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
    assert(method != "__event__");

    return _methodChannel.invokeMethod<T>(method, arguments);
  }

  Future<List<T>> invokeListMethod<T>(String method,
      [dynamic arguments]) async {
    assert(method != "__event__");

    return _methodChannel.invokeListMethod<T>(method, arguments);
  }

  Future<Map<K, V>> invokeMapMethod<K, V>(String method,
      [dynamic arguments]) async {
    assert(method != "__event__");

    return _methodChannel.invokeMapMethod<K, V>(method, arguments);
  }

  VoidCallback addEventListener(String name, EventListener listener) {
    assert(name != null && listener != null);

    List<EventListener> list = _eventListeners[name];
    if (list == null) {
      list = List();
      _eventListeners[name] = list;
    }

    list.add(listener);

    return () {
      list.remove(listener);
    };
  }

  VoidCallback addMethodHandler(MethodHandler handler) {
    assert(handler != null);

    _methodHandlers.add(handler);

    return () {
      _methodHandlers.remove(handler);
    };
  }
}

大概的梳理一下:

  • 定义了两个回调方法类型(类似Java中的Interface)
  • 定义了channel-name标识符并初始化MethodChannel
  • 一个Map,key是String,用来存储我们调用时设置的唯一值,类似于方法名啥的,value,是一个监听列表,因为这一个唯一值可能在多处使用(例如:在C页面更新了某个属性,需要在A页面和B页面也更新,于是对同一个监听name,就有多个Listener)
  • 一个Set,里面装了不少MethodHandler,看了一下相关代码,猜测他的用法就是:在监听的地方写下addMethodHandler,他的参数是一个MethodHandler,而MethodHandler的参数比较神奇,是MethodCall,也就是说他啥都没处理;就像这边的FlutterBoost是个总的消息汇集控制中心,而MethodHandler则属于它麾下子的控制中心,自己去根据MethodCall中收到的消息名,自己处理。
  • setMethodCallHandler是MethodChannel的回调监听,在里面判断如果是事件监听则在Map中找到对应的name,然后遍历调用他们的List中的Listener;如果不是事件监听是分发就是MethodHandler那种,就遍历Set,把事件分发出去(个人猜测:有点像广播,主要你注册了,当某个广播发出时,你就会收到);
  • 剩下的方法就是发送不同的事件;Event事件就用“–event–”标识一下;(应该是下划线,语法没找到不支持,懒得找了)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值