flutter插件开发需要了解的EventChannel与MethodChannel

在flutter插件开发中,EventChannelMethodChannel是两个不可避免的类。我们要了解它,最好先记住它通常用来干嘛。

MethodChannel用通俗的语言来描述它的作用就是,当你像在flutter端调用native功能的时候,可以用它。

EventChannel用通俗的语言来描述就是,当native想通知flutter层一些消息的时候,可以用它。

这两个channel是如何打通的

要用这两个桥梁,首先要明白它是怎么打通的,我们应该对这段代码不陌生:

public static void registerWith(Registrar registrar) {          TipFlutterImageViewPlugin plugin = new TipFlutterImageViewPlugin(registrar, registrar.context());          final MethodChannel channel = new MethodChannel(registrar.messenger(), "com.tencent.igame/flutter_image_view_method");          final EventChannel eventChannel = new EventChannel(registrar.messenger(), "com.tencent.igame/flutter_image_view_event");          channel.setMethodCallHandler(plugin);          eventChannel.setStreamHandler(plugin);      }

在你create一个插件工程的时候,flutter脚手架是会为你生成一个名字为XXXPlugin的类,里面就有这么一个registerWith的方法。两个Channel都是在这里注册的。那么,这段代码在何处被调用的呢?我们可以看看插件代码的android工程;

public final class GeneratedPluginRegistrant {    public static void registerWith(PluginRegistry registry) {      if (alreadyRegisteredWith(registry)) {        return;      }      XXXPlugin.registerWith(registry.registrarFor("com.tencent.igame.tip_flutter_image_view.TipFlutterImageViewPlugin"));    }      private static boolean alreadyRegisteredWith(PluginRegistry registry) {      final String key = GeneratedPluginRegistrant.class.getCanonicalName();      if (registry.hasPlugin(key)) {        return true;      }      registry.registrarFor(key);      return false;    }  }

然后这个静态方法在MainActivity的onCreate中被调用了。

protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      GeneratedPluginRegistrant.registerWith(this);    }

到registerWith被调用为止,两个Channel被注册了而已,它还并没有和flutter关联起来,此时我们也不急,先看看,我们可以了解到的是registerWith被调用了一次,因此XXXPlugin只有一个实例,MethodChannel和EventChannel是其成员,也只有一个。好,接下来,flutter关联上这了两个Channel。

flutter连接上Channel

eventChannel = new EventChannel('com.tencent.igame/flutter_image_view_event')          .receiveBroadcastStream(url)          .map((dynamic event) => _parseState(event));  methodChannel = new MethodChannel('com.tencent.igame/flutter_image_view_method');

一般可以放在flutter页面的initState做。

此时,我们flutter调用原生可以使用

//flutter端  _channel.invokeMethod('method',{})  //原生端   public void onMethodCall(MethodCall call, Result result) {          if (call.method.equals("create")) {              //xxxx          }  }

此时,原生发送消息到flutter可以

    //原生端发送消息      public void onListen(Object args, EventChannel.EventSink eventSink) {         eventSink.success()         //eventSink.error();      }        //flutter端接收消息      eventChannel.listen((data) {        //xxxx      });

至此,整个流程打通了,如果就这样完了的话,这篇文章可以改名叫做《EventChannel与MethodChannel的使用》了。

EventChannel的猫腻

EventChannel其实是基于MethodChannel实现的,不信,我们可以看源码:

Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {      final MethodChannel methodChannel = MethodChannel(name, codec);      StreamController<dynamic> controller;      controller = StreamController<dynamic>.broadcast(onListen: () async {        defaultBinaryMessenger.setMessageHandler(name, (ByteData reply) async {          if (reply == null) {            controller.close();          } else {            try {              controller.add(codec.decodeEnvelope(reply));            } on PlatformException catch (e) {              controller.addError(e);            }          }          return null;        });        try {          await methodChannel.invokeMethod<void>('listen', arguments);        } catch (exception, stack) {          //xxx/      }, onCancel: () async {        defaultBinaryMessenger.setMessageHandler(name, null);        try {          await methodChannel.invokeMethod<void>('cancel', arguments);        } catch (exception, stack) {          //xxxx      });      return controller.stream;    }  }

说的没错把,这里就是通过methodChannel去调用在原生那边实现的方法:

onListen(Object args, EventChannel.EventSink eventSink)  onCancel(Object args)

好的,我们的认识更进了一步,那么,在问一个问题,如果多次注册同一个EventChannel并listen,那么每个都有效吗?

就直接说答案了,并不是每个都有效,有效的只是最后注册的那一个。

还记得我们的plugin的实例只有一个吗?还记得其中的成员EventChannel也只有一个吗?那么,在flutter端调用多次

eventChannel = new EventChannel(&#39;com.tencent.igame/flutter_image_view_event&#39;)          .receiveBroadcastStream(url)          .map((dynamic event) => _parseState(event));

显然只有最后一次会生效,其他是收不到消息的。

EventChannel可以有多个吗?

这个当然是没问题的,多个EventChannel只需要在registerWith方法中注册一一进行注册就OK了。