Flutter作为一个跨平台的解决方案,在Android和iOS原生应用中往往以模块的形式嵌入,Flutter与Android原生的通信必不可少。所以本文将介绍Android是如何与flutter通信的。一、架构概述消息通过平台通道在native(host)和flutter(client)之间传递,如下图所示:为了保证用户界面能够正确响应,消息采用异步方式传递。是native发消息给flutter,还是flutter发消息给native。在flutter中,MethodChannel可以发送方法调用对应的消息。在原生平台,MethodChannel可以接收Android中的方法调用并返回结果。这些类帮助我们用很少的代码开发平台插件。注:本节内容来自flutter官网,读者可自行参考。2.平台通道数据类型支持和编解码器平台通道可以使用提供的编解码器对消息进行编码和解码,这些编解码器支持简单的类似JSON的值(例如布尔值、数字、字符串、字节缓冲区以及这些的列表和映射)的高效二进制序列化。当您发送和接收值时,它们会自动序列化和反序列化。下表显示了如何在平台端接收Dart值,反之亦然:关于编解码器,Android端提供了以下内容。BinaryCodec:最简单的编解码器,其返回值类型与输入参数相同,均为二进制格式(ByteBuffer)。因为BinaryCodec在编码和解码过程中什么都不做,它只是原封不动地返回二进制数据。因此,传输的数据在编码和解码时不会被复制。当传输的数据量比较大时,这种方法非常有用。比如从Android端传入一张图片,在Flutter端显示。StandardMessageCodec:是BasicMessageChannel的默认编解码器,支持基本数据类型、列表、字典等。编码时会先将数据写入ByteArrayOutputStream,然后再将流中的数据写入ByteBuffer。解码时直接从ByteBuffer中读取数据。StandardMethodCodec:是基于StandardMessageCodec的封装。是MethodChannel和EventChannel的默认编解码器。StringCodec:用于字符串与二进制数据之间的编解码,其编码格式为UTF-8。编码时,String会被转成字节数组,然后将数组写入到ByteBuffer中。解码时直接从ByteBuffer中读取数据JSONMessageCodec:内部调用StringCodec实现编码解码。JSONMethodCodec:基于JSONMessageCodec的封装。可以在MethodChannel和EventChannel中使用。ByteBuffer是Nio中的一个类,顾名思义——它是一个用于存储字节的区域。它有两个实现类——DirectByteBuffer和HeapByteBuffer。DirectByteBuffer直接在内存中开辟一块区域存放数据,而HeapByteBuffer在JVM堆中开辟一块区域存放数据,所以如果要让数据在DirectByteBuffer中与HeapByteBuffer进行通信,就需要一个副本。3.通信方式上面已经讲了Android和flutter通信的一些基础知识。下面进入正题,看看Android是如何与flutter通信的。Android与Flutter通信的实现方式有四种。由于在初始化flutter页面的时候传递了一个string—route,所以我们可以利用route做文章,传递我们要传递的数据。该方法只支持单向数据传输,数据类型只能是字符串,没有返回值。它是通过EventChannel实现的,只支持单向数据传输,没有返回值。通过MethodChannel实现,支持双向传输数据,有返回值。BasicMessageChannel实现,BasicMessageChannel支持双向传输数据,有返回值。让我们来看看这些方法的使用。3.1.初始化时传值主要是利用创建flutter页面传的路由来做文章。作者觉得这个方法是个trick,但是还是可以用来传递数据的。它的使用非常简单,代码如下。先看Android代码。//第三个参数可以替换成我们想要的字符串。FlutterViewflutterView=Flutter.createView(this,getLifecycle(),"route");在flutter中,我们只需要通过如下代码获取值即可。voidmain()=>runApp(MyApp(initParams:window.defaultRouteName,));classMyAppextendsStatelessWidget{finalStringinitParams;//是之前传递的值—routeMyApp({Keykey,@requiredthis.initParams}):super(key:key);@overrideWidgetbuild(BuildContextcontext){...}}这样Android就可以在flutter初始化的时候传数据给flutter了。由于runApp只会被调用一次,所以这个方法只能传递一次数据,而且数据只能是字符串。使用window的相关API需要引入dart:ui3.2包,EventChannelEventChannel是native向flutter发送数据的一种单向通信方式,flutter不能返回任何数据给native。主要用于native发送手机电量变化,网络连接变化,陀螺仪,传感器等进行flutter。它的使用如下。先看Android代码。publicclassEventChannelPluginimplementsEventChannel.StreamHandler{privatestaticfinalStringTAG=EventChannelPlugin.class.getSimpleName();privateEventChannel.EventSinkeventSink;privateActivityactivity;staticEventChannelPluginregisterWith(FlutterViewflutterView){EventChannelPluginplugin=newEventChannelPlugin(flutterView);newEventChannel(flutterView,"EventChannelPlugin").setStreamHandler(plugin);returnplugin;}privateEventChannelPlugin(FlutterViewflutterView){this.activity=(Activity)flutterView.getContext();}voidsend(Objectparams){if(eventSink!=null){eventSink.success(params);}}voidsendError(Stringstr1,Stringstr2,Objectparams){if(eventSink!=null){eventSink.error(str1,str2,params);}}voidcancel(){if(eventSink!=null){eventSink.endOfStream();}}//第一个参数为flutter初始化EventChannel时返回的值,仅此一次@OverridepublicvoidonListen(Objecto,EventChannel.EventSinkeventSink){this.eventSink=eventSink;Log.i(TAG,"eventSink:"+eventSink);Log.i(TAG,"Object:";+o.toString());Toast.makeText(activity,"onListen——obj:"+o,Toast.LENGTH_SHORT).show();}@OverridepublicvoidonCancel(Objecto){Log.i(TAG,"onCancel:"+o.toString());Toast.makeText(activity,"onCancel——obj:"+o,Toast.LENGTH_SHORT).show();this.eventSink=null;}}作者在Android上做了一个代码side简单的封装还是很好理解的。下面看一下flutter的代码实现。class_MyHomePageStateextendsState{EventChannel_eventChannelPlugin=EventChannel("EventChannelPlugin");StreamSubscription_streamSubscription;@overridevoidinitState(){_streamSubscription=_eventChannelPlugin//["abc",123,"Hello"监听参数]对应Android端的第一个on不能传递value.receiveBroadcastStream(["abc",123,"Hello"]).listen(_onToDart,onError:_onToDartError,onDone:_onDone);super.initState();}@overridevoiddispose(){if(_streamSubscription!=null){_streamSubscription.cancel();_streamSubscription=null;}super.dispose();}//native端发送正常数据void_onToDart(message){print(message);}//当native出错时,发送的datavoid_onToDartError(error){print(error);}//原生发送数据完成时调用的方法,void_onDone(){print("消息传递完成");}@overrideWidgetbuild(BuildContextcontext){...}}以上就是通过EventChannel进行通信的代码实现,调用EventChannelPlugin的send方法可以向flutter发送数据。3.3.MethodChannelMethodChannel是native和flutter之间发送数据的通信方法。顾名思义,通过MethodChannel可以调用native和flutter中对应的方法,这个方法是有返回值的。它的使用如下。先看Android端的代码实现。publicclassMethodChannelPluginimplementsMethodChannel.MethodCallHandler{privateActivityactivity;privateMethodChannelchannel;publicstaticMethodChannelPluginregisterWith(FlutterViewflutterView){MethodChannelchannel=newMethodChannel(flutterView,"MethodChannelPlugin");MethodChannelPluginmethodChannelPlugin=newMethodChannelPlugin((Activity)flutterView.getContext(),channel);channel.setMethodCallHandler(methodChannelPlugin);returnmethodChannelPlugin;}privateMethodChannelPlugin(Activityactivity,MethodChannelchannel){this.activity=activity;this.channel=channel;}//调用flutter端方法,无返回值publicvoidinvokeMethod(Stringmethod,Objecto){channel.invokeMethod(method,o);}//调用flutter端方法,有返回值publicvoidinvokeMethod(Stringmethod,Objecto,MethodChannel.Resultresult){channel.invokeMethod(method,o,result);}@OverridepublicvoidonMethodCall(MethodCallmethodCall,MethodChannel.Resultresult){switch(methodCall.method){case"send"://返回的方法名//给flutter端的返回值result.success("MethodChannelPlugin收到:"+methodCall.arguments);Toast.makeText(activity,methodCall.arguments+"",Toast.LENGTH_SHORT).show();if(activityinstanceofFlutterAppActivity){((FlutterAppActivity)activity).showContent(methodCall.arguments);}break;default:result.notImplemented();break;}}}作者对Android端代码做了简单的封装,还是很好理解的。下面看一下flutter的代码实现。class_MyHomePageStateextendsState{MethodChannel_methodChannel=MethodChannel("MethodChannelPlugin");@overridevoidinitState(){_methodChannel.setMethodCallHandler((handler)=>Future((){print("_methodChannel:${/handler}")";监听native发送的方法名和参数switch(handler.method){case"send":_send(handler.arguments);//handler.arguments表示nativebreak传递的方法参数;}}));super.initState();}//flutter调用原生方法void_send(arg){setState((){_content=arg;});}String_resultContent="";//flutter调用原生方法void_sendToNative(){Futurefuture=_methodChannel.invokeMethod("send",_controller.text);future.then((message){setState((){//message是native_resultContent="returnvalue:"+message;});});}@overrideWidgetbuild(BuildContextcontext){...}}以上就是通过MethodChannel进行通信的代码实现,还是比较简单的,在Android端,你只需要需要调用MethodChannelPlugin的invokeMethod方法。在flutter端使用,只需要参考_sendToNative方法的实现即可。3.4.BasicMessageChannelBasicMessageChannel是一种可以在native和flutter之间发送消息的通信方法。它支持的数据类型最多,应用范围最广。EventChannel和MethodChannel的应用场景使用BasicMessageChannel可以实现,但是BasicMessageChannel的应用场景使用EventChannel和MethodChannel不一定能实现。这个方法有一个返回值。它的使用如下。先看Android代码的实现。//这里支持的数据类型是String。publicclassBasicMessageChannelPluginimplementsBasicMessageChannel.MessageHandler{privateActivityactivity;privateBasicMessageChannelmessageChannel;staticBasicMessageChannelPluginregisterWith(FlutterViewflutterView){returnnewBasicMessageChannelPlugin(flutterView);}privateBasicMessageChannelPlugin(FlutterViewflutterView){this.activity=(Activity)flutterView.getContext();this.messageChannel=newBasicMessageChannel(flutterView,"BasicMessageChannelPlugin",StringCodec.INSTANCE);messageChannel.setMessageHandler(this);}@OverridepublicvoidonMessage(Strings,BasicMessageChannel.Replyreply){reply.reply("BasicMessageChannelPluginreceived:"+s);if(activityinstanceofFlutterAppActivity){((FlutterAppActivity)activity).showContent(s);}}voidsend(Stringstr,BasicMessageChannel.Replyreply){messageChannel.send(str,reply);}}作者在上面做了一个代码Android端一个简单的封装还是很好理解的。下面看一下flutter的代码实现。class_MyHomePageStateextendsState{//StringCodec()是编码格式BasicMessageChannel_basicMessageChannel=BasicMessageChannel("BasicMessageChannelPlugin",StringCodec());@overridevoidinitState(){_basicMessageChannel.setMessageHandler((>Fumessage