An article to understand the communication between Android and Flutter

As a cross-platform solution, Flutter is often embedded in native Android and iOS applications as a module. The communication between Flutter and the native Android is essential. So this article will tell you how Android communicates with flutter.

1. Architecture overview

The message is passed between native (host) and flutter (client) through the platform channel, as shown in the following figure:

To ensure that the user interface can respond correctly, messages are delivered asynchronously. Whether it is native sending messages to flutter, or flutter sending messages to native.

In flutter, MethodChannel can send messages corresponding to method calls. On the native platform, MethodChannel can receive method calls and return results on Android. These classes can help us develop platform plug-ins with very little code.

Note: The content of this section comes from the official website of flutter, and readers can refer to it by themselves.

2. Platform channel data type support and codec

The platform channel can use the provided codecs to encode and decode messages. These codecs support efficient binary serialization of simple JSON-like values, such as Boolean values, numbers, strings, byte buffers, and lists and mappings of these . When you send and receive values, these values ​​are automatically serialized and deserialized.

The following table shows how to receive Dart values ​​on the platform side, and vice versa:

Regarding the codec, the Android side provides the following four types.

  • BinaryCodec : It is the simplest kind of codec. Its return value type is the same as the input parameter type, both in binary format (ByteBuffer). Because BinaryCodec does nothing during the encoding and decoding process, it just returns the binary data intact. Therefore, the transferred data will be free from copying during encoding and decoding. This method is useful when the amount of transferred data is relatively large. For example, a picture is transferred from the Android side to the Flutter side for display.
  • StandardMessageCodec : It is the default codec of BasicMessageChannel and supports basic data types, lists and dictionaries. During encoding, data is written to the ByteArrayOutputStream stream first, and then the data in the stream is written to the ByteBuffer. When decoding, read data directly from ByteBuffer.
  • StandardMethodCodec : It is a package based on StandardMessageCodec. It is the default codec for MethodChannel and EventChannel.
  • StringCodec : It is used for encoding and decoding between string and binary data, and its encoding format is UTF-8. When encoding, the String is converted into a byte array, and then the array is written into the ByteBuffer. When decoding, read data directly from ByteBuffer
  • JSONMessageCodec : Internally calls StringCodec to implement encoding and decoding.
  • JSONMethodCodec : encapsulation based on JSONMessageCodec. Can be used in MethodChannel and EventChannel.

ByteBuffer is a class in Nio, as the name suggests-is an area for storing bytes. It has two implementation classes-DirectByteBuffer and HeapByteBuffer. DirectByteBuffer directly opens up an area in the memory to store data, and HeapByteBuffer opens up an area in the JVM heap to store data, so if you want data to communicate with HeapByteBuffer in DirectByteBuffer , You need to make a copy.

3. Communication method

I talked about some basic knowledge of the communication between Android and flutter. Now let's enter the topic and see how Android communicates with flutter.

There are four ways to implement communication between Android and Flutter.

  1. Since a string—route is passed when initializing the flutter page, we can use route as an article to pass the data we want to pass. This method only supports one-way data transfer and the data type can only be a string, without a return value.
  2. Realized by EventChannel, EventChannel only supports one-way transmission of data without return value.
  3. Realized by MethodChannel, MethodChannel supports two-way transmission of data and has a return value.
  4. Realized by BasicMessageChannel, BasicMessageChannel supports two-way transmission of data and has a return value.

Let's take a look at the use of these methods.

3.1. Pass value during initialization

It is mainly used to create a flutter page to pass the route to make a fuss. The author thinks that this method is a trick, but it can still be used to pass data. Its use is very simple, the code is as follows.

First look at the Android code.

//第三个参数可以换成我们想要字符串。
FlutterView flutterView = Flutter.createView(this, getLifecycle(), "route");

In flutter, we only need to get the value through the following code.

void main() => runApp(MyApp(
 initParams: window.defaultRouteName,
 ));
class MyApp extends StatelessWidget {
 final String initParams;//既是前面传递的值——route
 MyApp({Key key, @required this.initParams}) : super(key: key);
 @override
 Widget build(BuildContext context) {...}
}

In this way, Android can pass data to flutter when initializing flutter. Since runApp will only be called once, this method can only pass data once and the data can only be a string.

Use window related APIs need to import package dart:ui

3.2、EventChannel

EventChannel is a one-way communication method for native to send data to flutter, and flutter cannot return any data to native. Mainly used for native to send mobile phone battery changes, network connection changes, gyroscopes, sensors, etc. to flutter. It is used as follows.

First look at the Android code.

public class EventChannelPlugin implements EventChannel.StreamHandler {
 private static final String TAG = EventChannelPlugin.class.getSimpleName();
 private EventChannel.EventSink eventSink;
 private Activity activity;
 static EventChannelPlugin registerWith(FlutterView flutterView) {
 EventChannelPlugin plugin = new EventChannelPlugin(flutterView);
 new EventChannel(flutterView, "EventChannelPlugin").setStreamHandler(plugin);
 return plugin;
 }
 private EventChannelPlugin(FlutterView flutterView) {
 this.activity = (Activity) flutterView.getContext();
 }
 void send(Object params) {
 if (eventSink != null) {
 eventSink.success(params);
 }
 }
 void sendError(String str1, String str2, Object params) {
 if (eventSink != null) {
 eventSink.error(str1, str2, params);
 }
 }
 void cancel() {
 if (eventSink != null) {
 eventSink.endOfStream();
 }
 }
 //第一个参数为flutter初始化EventChannel时返回的值,仅此一次
 @Override
 public void onListen(Object o, EventChannel.EventSink eventSink) {
 this.eventSink = eventSink;
 Log.i(TAG, "eventSink:" + eventSink);
 Log.i(TAG, "Object:" + o.toString());
 Toast.makeText(activity, "onListen——obj:" + o, Toast.LENGTH_SHORT).show();
 }
 @Override
 public void onCancel(Object o) {
 Log.i(TAG, "onCancel:" + o.toString());
 Toast.makeText(activity, "onCancel——obj:" + o, Toast.LENGTH_SHORT).show();
 this.eventSink = null;
 }
}

The author made a simple encapsulation of the Android code, which is still well understood. Let's look at the implementation of flutter code below.

class _MyHomePageState extends State<MyHomePage> {
 EventChannel _eventChannelPlugin = EventChannel("EventChannelPlugin");
 StreamSubscription _streamSubscription;
 @override
 void initState() {
 _streamSubscription = _eventChannelPlugin
 //["abc", 123, "你好"]对应着Android端onListen方法的第一个参数,可不传值
 .receiveBroadcastStream(["abc", 123, "你好"])
 .listen(_onToDart, onError: _onToDartError, onDone: _onDone);
 super.initState();
 }
 @override
 void dispose() {
 if (_streamSubscription != null) {
 _streamSubscription.cancel();
 _streamSubscription = null;
 }
 super.dispose();
 }
 //native端发送正常数据
 void _onToDart(message) {
 print(message);
 }
 //当native出错时,发送的数据
 void _onToDartError(error) {
 print(error);
 }
 //当native发送数据完成时调用的方法,每一次发送完成就会调用
 void _onDone() {
 print("消息传递完毕");
 }
 @override
 Widget build(BuildContext context) {...}
}

The above is the implementation of the code for communicating through EventChannel. Calling the send method of EventChannelPlugin can send data to flutter.

3.3、MethodChannel

MethodChannel is a communication method for sending data between native and flutter. As the name suggests, the corresponding method in native and flutter can be called through MethodChannel. This method has a return value. It is used as follows.

First look at the code implementation on the Android side.

public class MethodChannelPlugin implements MethodChannel.MethodCallHandler {
 private Activity activity;
 private MethodChannel channel;
 public static MethodChannelPlugin registerWith(FlutterView flutterView) {
 MethodChannel channel = new MethodChannel(flutterView, "MethodChannelPlugin");
 MethodChannelPlugin methodChannelPlugin = new MethodChannelPlugin((Activity) flutterView.getContext(), channel);
 channel.setMethodCallHandler(methodChannelPlugin);
 return methodChannelPlugin;
 }
 private MethodChannelPlugin(Activity activity, MethodChannel channel) {
 this.activity = activity;
 this.channel = channel;
 }
 //调用flutter端方法,无返回值
 public void invokeMethod(String method, Object o) {
 channel.invokeMethod(method, o);
 }
 //调用flutter端方法,有返回值
 public void invokeMethod(String method, Object o, MethodChannel.Result result) {
 channel.invokeMethod(method, o, result);
 }
 @Override
 public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
 switch (methodCall.method) {
 case "send"://返回的方法名
 //给flutter端的返回值
 result.success("MethodChannelPlugin收到:" + methodCall.arguments);
 Toast.makeText(activity, methodCall.arguments + "", Toast.LENGTH_SHORT).show();
 if (activity instanceof FlutterAppActivity) {
 ((FlutterAppActivity) activity).showContent(methodCall.arguments);
 }
 break;
 default:
 result.notImplemented();
 break;
 }
 }
}

The author made a simple encapsulation of the Android code, which is still well understood. Let's look at the implementation of flutter code below.

class _MyHomePageState extends State<MyHomePage> {
 MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
 @override
 void initState() {
 _methodChannel.setMethodCallHandler((handler) => Future<String>(() {
 print("_methodChannel:${handler}");
 //监听native发送的方法名及参数
 switch (handler.method) {
 case "send":
 _send(handler.arguments);//handler.arguments表示native传递的方法参数
 break;
 }
 }));
 super.initState();
 }
 //native调用的flutter方法
 void _send(arg) {
 setState(() {
 _content = arg;
 });
 }
 String _resultContent = "";
 //flutter调用native的相应方法
 void _sendToNative() {
 Future<String> future =
 _methodChannel.invokeMethod("send", _controller.text);
 future.then((message) {
 setState(() {
 //message是native返回的数据
 _resultContent = "返回值:" + message;
 });
 });
 }
 @override
 Widget build(BuildContext context) {...}
}

The above is the code implementation of communication through MethodChannel. It is relatively simple. To use it on the Android side, you only need to call the invokeMethod method of MethodChannelPlugin. To use it on the flutter side, you only need to refer to the implementation of the _sendToNative method.

3.4、BasicMessageChannel

BasicMessageChannel is a communication method that can send messages between native and flutter. It supports the most data types and has the widest range of use. The application scenarios of EventChannel and MethodChannel can be implemented using BasicMessageChannel, but the application scenarios of BasicMessageChannel may not necessarily be implemented using EventChannel and MethodChannel. This method has a return value. It is used as follows.

First look at the implementation of Android code.

//这里支持的数据类型为String。
public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String> {
 private Activity activity;
 private BasicMessageChannel<String> messageChannel;
 static BasicMessageChannelPlugin registerWith(FlutterView flutterView) {
 return new BasicMessageChannelPlugin(flutterView);
 }
 private BasicMessageChannelPlugin(FlutterView flutterView) {
 this.activity = (Activity) flutterView.getContext();
 this.messageChannel = new BasicMessageChannel<String>(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
 messageChannel.setMessageHandler(this);
 }
 @Override
 public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {
 reply.reply("BasicMessageChannelPlugin收到:" + s);
 if (activity instanceof FlutterAppActivity) {
 ((FlutterAppActivity) activity).showContent(s);
 }
 }
 void send(String str, BasicMessageChannel.Reply<String> reply) {
 messageChannel.send(str, reply);
 }
}

The author made a simple encapsulation of the Android code, which is still well understood. Let's look at the implementation of flutter code below.

class _MyHomePageState extends State<MyHomePage> {
 //StringCodec()为编码格式
 BasicMessageChannel<String> _basicMessageChannel =
 BasicMessageChannel("BasicMessageChannelPlugin", StringCodec());
 @override
 void initState() {
 _basicMessageChannel.setMessageHandler((message) => Future<String>(() {
 print(message);
 //message为native传递的数据
 setState(() {
 _content = message;
 });
 //给Android端的返回值
 return "收到Native消息:" + message;
 }));
 _controller = TextEditingController();
 super.initState();
 }
 //向native发送消息
 void _sendToNative() {
 Future<String> future = _basicMessageChannel.send(_controller.text);
 future.then((message) {
 _resultContent = "返回值:" + message;
 });
 }
 @override
 Widget build(BuildContext context) {...}
}

The above is the code implementation of communication through BasicMessageChannel. On the Android side, you only need to call the send method of BasicMessageChannelPlugin to send data to flutter. BasicMessageChannel.Reply is the callback method of the return value. To use it on the flutter side, you only need to refer to the implementation of the _sendToNative method.

4, communication principle

From the analysis of the source code of the communication between Android and Flutter, the implementation is relatively simple, using ByteBuffer as the data carrier, and then sending and receiving data through BinaryMessenger. The overall design is as follows.

It can be seen from the figure that the same design is adopted on the Android side and the flutter side. As mentioned earlier, communication is performed asynchronously, so where is the thread switching? In fact, it is implemented at the bottom of the system. In the communication between Android and Flutter, the bottom layer of the system shields a large number of complex operations such as thread switching and data copying. This allows the Android side and the flutter side to communicate easily.

On the Android side, BinaryMessenger is an interface, which is implemented in FlutterView, and communicates with the bottom layer of the system through JNI in the method of BinaryMessenger. On the Flutter side, BinaryMessenger is a class whose function is to communicate with the class window, and the class window really communicates with the bottom layer of the system.

For the underlying implementation of communication, you can read Xianyu's technical article-an in-depth understanding of Flutter Platform Channel. This article is a good introduction to the underlying principles of the communication between Flutter and Native.

5. Summary

In the mixed development mode of Android and Flutter, there are certainly no less scenarios for communication between them. Understanding the various ways and uses of communication between Android and Flutter will help you choose a reasonable way to achieve it.

6. Finally

Of course, the knowledge points listed in this article are not yet complete, and we still need to learn more systematically. I just have a copy of Flutter learning materials here.

There is also an Android learning PDF + architecture video + source code notes collected by the big guys , advanced architecture technology advanced brain map, Android development interview special materials, advanced advanced architecture materials

These are the fine materials that I will read again and again in my spare time. There are detailed explanations on the high-frequency knowledge points of interviews with major factories in recent years. I believe it can effectively help everyone master knowledge and understand principles.

Of course, you can also use it to check for omissions and improve your competitiveness.

If you need it, you can click to get it !

If you like this article, you might as well give me a like, leave a message in the comment area, or forward and support it~

Guess you like

Origin blog.csdn.net/ajsliu1233/article/details/108299127