catalogue

  • 01. Interaction between flutter and native
  • 02. MethodChanel process
  • 03.MethodChanel Use process
  • 04.MethodChanel Code practice
  • 05. EventChannel process
  • 06. Basic process of EventChannel
  • 07.EventChannel code implementation
  • 08. BasicMessageChannel process
  • 09.BasicMessageChannel
  • 10.BasicMessageChannel code implementation
  • 11.Channel codec description
  • 12. Can Channel communication be subthreaded
  • 13. Stability of Channel communication transmission
  • 14. How to implement onActivityResult

recommended

  • Fluter Utils utility library: github.com/yangchong21…
  • The Flutter hybrid project code example: github.com/yangchong21…

01. Interaction between flutter and native

1.1 Interaction Overview

  • Official means of communication
    • Look at the picture. Channel communication
    • At the bottom, Flutter and its platforms communicate by sending asynchronous BinaryMessages. This basic communication is implemented by BinaryMessages on Flutter and BinaryMessenger on Android. Its specific implementation is FlutterNativeView, which is a protocol FlutterBinaryMessenger on iOS. FlutterViewController complies with and implements this protocol.
  • The flutter can communicate with Native to help us use the capabilities provided by Native.
    • Communication is bidirectional. We can call dart code of the Flutter layer from the Native layer and also call Native code from the flutter layer.
  • We need to use Platform Channels APIs for communication, including the following three types:
    • MethodChannel: Method Invocation
    • EventChannel: For event streams
    • BasicMessageChannel: Used to pass strings and semi-structured messages. What is semi-structured here? It will be explained below…
  • Channel Is communication asynchronous or synchronous
    • In order to ensure the smoothness of user interface during interaction, both messages sent from Flutter to Native and messages sent from Native to Flutter are delivered asynchronously. So why not use synchronization, which will be discussed below…
  • Analysis of several channel application scenarios
    • Usage scenario of MethodChannel: Both the Flutter and Native end can use MethodChannel to send predefined method names to the other platform to invoke the corresponding message processing logic of the other platform and return the return value to the called party.
    • The usage scenario of EventChannel is as follows: The Native platform actively sends messages to the Flutter platform in a one-way manner. The Flutter cannot return any data to the Native end. EventChannel description is unidirectional. Compare it to broadcasting on Android…
    • BasicMessageChannel usage scenarios: After taking photos such as flutter want to take pictures, pictures of the path needs to flutter, the path of the photos sent can use BasicMessageChannel. Reply Reply, also can use sendMessage initiative to send a message. Personally think that receives the message and Reply message belongs to a communication, so I tend to use BasicMessageChannel. Reply.
  • Mixed development usually uses that channel
    • However, mixed development usually involves frequent communication between the two ends, and individuals prefer to use BasicMessageChannel, which is more convenient to use and communicate without regard to the client.

1.2 Key notes on core Classes

  • MethodCall
    • Method invocation Java layer encapsulation, mainly data classes
  • MethodChannel
    • This primary user communicates with the Dart method, the class
  • MethodCallHandler
    • The Java layer handles the DART layer time interface, which is the upper-layer interface in the communication protocol
  • BinaryMessageHandler
    • Java layer and DART layer communication of the lowest abstract interface, for binary packet, interface
  • DartMessenger
    • The lowest level is used to receive data sent from JNI. The implementation class
  • DartExecutor
    • Configure, boot, and start executing the Dart code. BinaryMessenger concrete implementation class
  • FlutterView
    • NA The container view used to host the flutter
  • IncomingMethodCallHandler
    • An implementation class for BinaryMessageHandler, in which the user receives packets sent from the bottom layer, forwards them to MethodCallHandler, and packages the results sent by MethodCallHandler to the Dart layer. The implementation class
  • FlutterJNI
    • The JNI layer encapsulation is used to communicate with the underlying engine side

02. MethodChannel process

  • The most common of these is MethodChanel, which is used very similar to JNI calls on Android, but MethodChanel is simpler and asynchronous compared to JNI’s synchronous calls to MethodChanel:
  • As can be seen from the diagram of the flutter architecture, the communication between the flutter and native takes place between the Framework and Engine, and the MethodChannel will exchange data with Engine in the form of BinaryMessage inside the framewrok.

03.MethodChanel Use process

3.1 Flutter calls native

  • The flutter calls the native step
    • [native] registers a callback with MethodChannel#setMethodCallHandler
    • [flutter] initiates an asynchronous invocation with MethodChannel#invokeMethod
    • [native] Calls native methods to return Result in Result#success, or error in case of an error
    • [flutter] received the Result returned by Native
  • As is shown in

3.2 Native calls flutter

  • Native calls to flutter
    • This is in exactly the same order that flutter calls native, except that [native] reverses the role of flutter
  • As is shown in
  • The NA side uses MethodChannel
    • First define the Channel name, which needs to be unique. Use the same name on the Flutter side to create a MethodChannel. If the names are different, no match will be found……
    • The first parameter: is messenger, type is BinaryMessenger, is an interface, represents the message messenger, is the tool for sending and receiving messages;
    • The second argument is name, the Channel name, as defined by flutter.
    • The third parameter is codec, of type MethodCodec, which represents the codec for the message. If this parameter is not passed, StandardMethodCodec is used by default.

04.MethodChanel Code practice

4.1 Native calls flutter

  • Once you’ve defined a MethodChannel, call the setMethodCallHandler() method to set up the message handling callback, taking the MethodHandler type and implementing its onMethodCall() method. The onMethodCall() method has two arguments, methodCall and result. MethodCall records the information about the called method, including the method name and parameters. Result is the return value of the method, which can be returned to the Flutter end via result.success().
    private void createChannel() { nativeChannel = new MethodChannel(binaryMessenger, METHOD_CHANNEL, StandardMethodCodec.INSTANCE); / / register Handler implementation nativeChannel. SetMethodCallHandler (new MethodChannel. MethodCallHandler () {@ Override public void onMethodCall(@NonNull MethodCall methodCall, @nonnull methodChannel.result Result) {if ("android".equals(methodcall.method)) {// Receive the instruction from flutter String flutter = methodCall.argument("flutter"); Result.success ("Na received instruction "); // Return the argument to flutter result.success("Na received instruction "); }}}); }Copy the code
  • You can make NA perform a call to the flutter method by using the invokeMethod method. The Result interface is used to upload data to the flutter method as follows:
    HashMap<String , String> map = new HashMap<>(); Map. put("invokeKey"," Hello, this is the data from NA "); //nativeChannel.resizeChannelBuffer(100); nativeChannel.invokeMethod("getFlutterResult", map , new MethodChannel.Result() { @SuppressLint("SetTextI18n") @Override public void success(@Nullable Object result) { Tvcontent.settext (" test content: "+result); } @SuppressLint("SetTextI18n") @Override public void error(String errorCode, @Nullable String errorMessage, @nullable Object errorDetails) {tvContent.settext (" Test content: flutter passed to NA failed "); } @Override public void notImplemented() { } });Copy the code
  • Event receiving handler
    • OnMethodCall (MethodCall Call, methodChannel.result Result) receives information from the event sender using MethodCall and sends the Result to the event sender using Result.
    • Use methodcall. method: to distinguish between function names (method names) to execute different business logic,
    • HasArgument (“key”) : Checks whether there is a value corresponding to a key
    • Methodcall. argument (“key”) : Gets the value of the key
    • Result.success (object) : Returns the processed result to the event sender
  • Event sender
    • Event handling the sender through methodChannel. InvokeMethod (” name “, “to pass parameters”) of the need to pass arguments to event listeners. Among them
    • Method name: cannot be empty
    • Parameter to pass: may be null, or otherwise must be a JSON-serializable object.
    • Callback: can be empty. If not empty, it indicates the callback listening status after the FLUTTER method has been executed

4.2 Flutter calls native

  • Flutter using MethodChannel
    • The Flutter side also needs to define a MethodChannel. To use MethodChannel, you need to import the services.dart package with the same Channel name as the Android side.
    static const method = const MethodChannel('com.ycbjie.android/method');
    Copy the code
  • Add a listener for NA to call the flutter method. The flutter code is implemented by setMethodCallHandler. Return indicates data operations that a flutter sends back to NA.
    method.setMethodCallHandler(nativeCallHandler); // Register method, Future< Dynamic > nativeCallHandler(MethodCall MethodCall) Async {switch (methodCall.method) {case "GetFlutterResult ": // Get parameter String paramsFromNative = await methodCall.arguments["invokeKey"]; Print (" ------ $paramsFromNative"); Return "Hello, this is data from a flutter back to NA "; break; }}Copy the code
  • How does flutter send messages to NA by directly calling the invokeMethod method as shown below
    Future<Null> _jumpToNativeWithParams1() async {Map<String, String> Map = {"flutter": "this is a parameter from the flutter"}; String result = await method.invokeMethod('android', map); print(result); }Copy the code

05. EventChannel process

  • EventChannel is used to send notification events to the flutter from the native place, for example, the flutter listens for Android gravity changes, etc. Unlike MethodChannel, EventChannel is a one-way call native to flutter. The call is multicast (one-to-many), which is analogous to Android’s Brodecast broadcast.

06. Basic process of EventChannel

  • As usual, let’s take a look at the basic flow of API usage:
    • [native]EventChannel#setStreamHandler Register Handler implementation
    • After the [native]EventChannel is initialized, the EventSink reference is retrieved and saved in the StreamHandler#onLister callback
    • [flutter]EventChannel#receiveBroadcastStream Register a listener to establish a listener
    • [native] sends notification events using EventSink#sucess
    • [FLUTTER] received an event notification
    • [Native] Call endOfStream when the notification ends
  • As is shown in

07.EventChannel code implementation

  • The flutter end
    • Create an EventChannel and register the channel name for package name/Identifier
    • Register a listener with Streamsubscript #listen, where the cancelOnError parameter indicates whether the listener is automatically terminated when an error is encountered
    class _MyHomePageState extends State<MyHomePage> { static const EventChannel _channel = const EventChannel('com.example.eventchannel/interop'); StreamSubscription _streamSubscription; String _platformMessage; void _enableEventReceiver() { _streamSubscription = _channel.receiveBroadcastStream().listen( (dynamic event) { print('Received event: $event'); setState(() { _platformMessage = event; }); }, onError: (dynamic error) { print('Received error: ${error.message}'); }, cancelOnError: true); } void _disableEventReceiver() { if (_streamSubscription ! = null) { _streamSubscription.cancel(); _streamSubscription = null; } } @override initState() { super.initState(); _enableEventReceiver(); } @override void dispose() { super.dispose(); _disableEventReceiver(); }Copy the code
  • Native (android)
    • Register the Handler implementation with EventChannel#setStreamHandler
    • After initialization, get the eventSink reference and save it
    • EventSink Sends event notification
    • When the notification ends, event#endOfStream is called, at which point onCancel is called
    • Error notifications can be sent via evnet ink#error if necessary, and streamsubscript #onError of flutter will be notified
    class MainActivity: FlutterActivity() { private lateinit var channel: EventChannel var eventSink: EventSink? = null override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine) channel = EventChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.eventchannel/interop") channel.setStreamHandler( object : StreamHandler { override fun onListen(arguments: Any? , events: EventSink) { eventSink = events Log.d("Android", "EventChannel onListen called") Handler().postDelayed({ eventSink? .success("Android") //eventSink? .endOfStream() //eventSink? .error("error code", "error message","error details") }, 500) } override fun onCancel(arguments: Any?) { Log.w("Android", "EventChannel onCancel called") } }) } }Copy the code

08. BasicMessageChannel process

  • BasicMessageChannel is used to send messages to each other within a flutter and native. One party sends a message to the other party and gives a reply when the message is received.

09.BasicMessageChannel

  • The flutter sends messages to native
    • Create BasicMessageChannel [because]
    • [native] register Handler with BasicMessageChannel#MessageHandler
    • [flutter] sends messages via BasicMessageChannel#send
    • [native]BasicMessageChannel#MessageHandler#onMessage receives the message and then replies
  • As is shown in
  • Native sends messages to flutter
    • The process is the same, except that the flutter is reversed from the native
  • As is shown in

10.BasicMessageChannel code implementation

10.1 the flutter end

  • Flutter needs to do the following
    • Create BasicMessageChannel
    • Send a message via BasicMessageChannel#send
  • In contrast to other Channel types, MessageChannel creation requires specifying the encoding in addition to the Channel name:
    BasicMessageChannel(String name, MessageCodec<T> codec, {BinaryMessenger binaryMessenger})
    Copy the code
  • The sent message is processed in binary form, so binary encoding is required for different types of numbers
    • Encoding type message format
    • BinaryCodec when sending a binary message
    • JSONMessageCodec sends a JSON-formatted message
    • StandardMessage Dec sends basic type data
    • StringCodec sends a String message
  • code
    class _MyHomePageState extends State<MyHomePage> {
      static const _channel = BasicMessageChannel('com.ycbjie.android/basic', StringCodec());
    
      String _platformMessage;
    
      void _sendMessage() async {
        final String reply = await _channel.send('Hello World form Dart');
        print(reply);
      }
    
      @override
      initState() {
        super.initState();
    
        // Receive messages from platform
        _channel.setMessageHandler((String message) async {
          print('Received message = $message');
          setState(() => _platformMessage = message);
          return 'Reply from Dart';
        });
    
        // Send message to platform
        _sendMessage();
      }
    Copy the code

10.2 native (android)

  • The Android side does the following:
    • Create BasicMessageChannel
    • Register MessageHandler with setHandler
    • MessageHandler#onMessage after receiving a message in the callback, reply via reply
  • code
    class MainActivity: FlutterActivity() {
        override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
            GeneratedPluginRegistrant.registerWith(flutterEngine)
    
            val channel = BasicMessageChannel(
                    flutterEngine.dartExecutor.binaryMessenger,
                    "com.ycbjie.android/basic", StringCodec.INSTANCE)
    
            // Receive messages from Dart
            channel.setMessageHandler { message, reply ->
                Log.d("Android", "Received message = $message")
                reply.reply("Reply from Android")
            }
    
            // Send message to Dart
            Handler().postDelayed({
                channel.send("Hello World from Android") { reply ->
                    Log.d("Android", "$reply")
                }
            }, 500)
        }
    }
    Copy the code

11.Channel codec description

11.1 What is a message codec

  • What is a message codec
    • There is communication between Flutter and the platform, but the data sent and received is binary. This requires developers to consider more details, such as byte order (big and small) and how to represent more advanced message types, such as strings, maps, etc.
    • Thus, Flutter also provides a message Codec (Codec) for conversion between high-level data types (strings, maps, etc.) and binary data (Byte), that is, serialization and deserialization of messages.
  • What kinds of message codecs are there
    • MethodCodec: Codec abstraction, interface for method passing
    • JSONMethodCodec: An implementation class for MethodCodec that packages the data as a JSON structure and sends it to the Dart class
    • StandardMethodCodec: The implementation class for MethodCodec, which packages the data in default format and sends it to the Dart class

11.2 Four message codec types

  • BinaryCodec
    • MessageCodec implementation class, send binary data directly
    • BinaryCodec is the simplest Codec because its return value type is the same as the input parameter type, both in binary format (ByteBuffer on Android, NSData on iOS). In fact, BinaryCodec does nothing in the codec process, just returns the binary data message as it is. BinaryCodec may not make sense to you, but it can be very useful in situations such as when passing blocks of memory without copying them in the codec phase.
  • StringCodec
    • The implementation class of MessageCodec, responsible for decoding and encoding String messages
    • The String data is encoded and decoded in UTF-8 format and converted to java.util.String type on Android platform
  • JSONMessageCodec
    • Implementation class of MessageCodec, responsible for decoding and encoding jSON-type messages
    • JSONMessageCodec is used to handle JSON data types (strings, numbers, Booleans, null, arrays containing only these types, and maps containing keys of string and values of these types). During encoding, the data is converted to JSON strings. It is then converted to byte in UTF-8 format.
  • StandardMessageCodec
    • The implementation class of MessageCodec, responsible for decoding and encoding messages of the default type
    • StandardMessageCodec can be thought of as an updated version of JSONMessageCodec, which can handle data types that are a little more general than JSONMessageCodec, and when it comes to ints, Int is converted to platform-side 32-bit (int) or 64-bit (long) depending on the size of the int. StandardMessageCodec is also the default codec of the Flutter Platform Channel

11.3 Encoder source code analysis

  • First take a look at MessageCodec
    abstract class MessageCodec<T> {
    
      ByteData encodeMessage(T message);
    
      T decodeMessage(ByteData message);
    }
    Copy the code

11.4 the StandardMessageCodec

  • StandardMessageCodec is a little more complicated
    • So StandardMessageCodec, when it writes data, it says the definition of the type value that’s being written to that data, and then it writes the specific value that’s being written to that data, what does that mean?
  • To see how to write a value of the specified type, the code looks like this:
    protected void writeValue(ByteArrayOutputStream stream, Object value) { if (value == null || value.equals(null)) { stream.write(NULL); } else if (value == Boolean.TRUE) { stream.write(TRUE); } else if (value == Boolean.FALSE) { stream.write(FALSE); } else if (value instanceof Number) { if (value instanceof Integer || value instanceof Short || value instanceof Byte) {  stream.write(INT); writeInt(stream, ((Number) value).intValue()); } else if (value instanceof Long) { stream.write(LONG); writeLong(stream, (long) value); } else if (value instanceof Float || value instanceof Double) { stream.write(DOUBLE); writeAlignment(stream, 8); writeDouble(stream, ((Number) value).doubleValue()); } else if (value instanceof BigInteger) { stream.write(BIGINT); writeBytes(stream, ((BigInteger) value).toString(16).getBytes(UTF8)); } else { throw new IllegalArgumentException("Unsupported Number type: " + value.getClass()); } } else if (value instanceof String) { stream.write(STRING); writeBytes(stream, ((String) value).getBytes(UTF8)); } else if (value instanceof byte[]) { stream.write(BYTE_ARRAY); writeBytes(stream, (byte[]) value); } else if (value instanceof int[]) { stream.write(INT_ARRAY); final int[] array = (int[]) value; writeSize(stream, array.length); writeAlignment(stream, 4); for (final int n : array) { writeInt(stream, n); } } else if (value instanceof long[]) { stream.write(LONG_ARRAY); final long[] array = (long[]) value; writeSize(stream, array.length); writeAlignment(stream, 8); for (final long n : array) { writeLong(stream, n); } } else if (value instanceof double[]) { stream.write(DOUBLE_ARRAY); final double[] array = (double[]) value; writeSize(stream, array.length); writeAlignment(stream, 8); for (final double d : array) { writeDouble(stream, d); } } else if (value instanceof List) { stream.write(LIST); final List<? > list = (List) value; writeSize(stream, list.size()); for (final Object o : list) { writeValue(stream, o); } } else if (value instanceof Map) { stream.write(MAP); final Map<? ,? > map = (Map) value; writeSize(stream, map.size()); for (final Entry<? ,? > entry : map.entrySet()) { writeValue(stream, entry.getKey()); writeValue(stream, entry.getValue()); } } else { throw new IllegalArgumentException("Unsupported value: " + value); }}Copy the code
  • To see how to read a value of the specified type, the code looks like this:
      protected Object readValueOfType(byte type, ByteBuffer buffer) {
        final Object result;
        switch (type) {
          case NULL:
            result = null;
            break;
          case TRUE:
            result = true;
            break;
          case FALSE:
            result = false;
            break;
          case INT:
            result = buffer.getInt();
            break;
          case LONG:
            result = buffer.getLong();
            break;
          case BIGINT:
            {
              final byte[] hex = readBytes(buffer);
              result = new BigInteger(new String(hex, UTF8), 16);
              break;
            }
          case DOUBLE:
            readAlignment(buffer, 8);
            result = buffer.getDouble();
            break;
          case STRING:
            {
              final byte[] bytes = readBytes(buffer);
              result = new String(bytes, UTF8);
              break;
            }
          case BYTE_ARRAY:
            {
              result = readBytes(buffer);
              break;
            }
          case INT_ARRAY:
            {
              final int length = readSize(buffer);
              final int[] array = new int[length];
              readAlignment(buffer, 4);
              buffer.asIntBuffer().get(array);
              result = array;
              buffer.position(buffer.position() + 4 * length);
              break;
            }
          case LONG_ARRAY:
            {
              final int length = readSize(buffer);
              final long[] array = new long[length];
              readAlignment(buffer, 8);
              buffer.asLongBuffer().get(array);
              result = array;
              buffer.position(buffer.position() + 8 * length);
              break;
            }
          case DOUBLE_ARRAY:
            {
              final int length = readSize(buffer);
              final double[] array = new double[length];
              readAlignment(buffer, 8);
              buffer.asDoubleBuffer().get(array);
              result = array;
              buffer.position(buffer.position() + 8 * length);
              break;
            }
          case LIST:
            {
              final int size = readSize(buffer);
              final List<Object> list = new ArrayList<>(size);
              for (int i = 0; i < size; i++) {
                list.add(readValue(buffer));
              }
              result = list;
              break;
            }
          case MAP:
            {
              final int size = readSize(buffer);
              final Map<Object, Object> map = new HashMap<>();
              for (int i = 0; i < size; i++) {
                map.put(readValue(buffer), readValue(buffer));
              }
              result = map;
              break;
            }
          default:
            throw new IllegalArgumentException("Message corrupted");
        }
        return result;
      }
    Copy the code

11.5 How to Select an Appropriate Codec

  • The codec implementation class is not complex
    • You can first understand this more can better understand the data transfer, in fact, not Java upper use that way, the final transmission to the bottom data are fixed format, the agreement is unified data format can be identified by both sides, normally speaking, it is ok to use the default codec format.
  • About the four decoder usage scenarios
    • BinaryCodec
      • The used scenario is not found yet
    • StringCodec
      • This is useful for sending a single string of data with a single amount of data, such as LifecycleChannel
      public void appIsInactive() {
        Log.v(TAG, "Sending AppLifecycleState.inactive message.");
        channel.send("AppLifecycleState.inactive");
      }
      Copy the code
    • JSONMessageCodec
      • Suitable for complex data volumes, such as a pass with multiple data fields, such as KeyEventChannel
      public void keyDown(@NonNull FlutterKeyEvent keyEvent) {
        Map<String, Object> message = new HashMap<>();
        message.put("type", "keydown");
        message.put("keymap", "android");
        encodeKeyEvent(keyEvent, message);
       
        channel.send(message);
      }
      Copy the code
    • StandardMessageCodec
      • Default data codec, most of the time use the default is ok. For example: MethodChannel, EventChannel

12. Can Channel communication be subthreaded

12.1 Android Sends communication Messages

  • First look at the Android send communication information, analysis the main entrance is: nativeChannel. InvokeMethod (” setNum “, a, null);
    public void invokeMethod(String method, @Nullable Object arguments, Result callback) {
    messenger.send(
        name,
        codec.encodeMethodCall(new MethodCall(method, arguments)),
        callback == null ? null : new IncomingResultHandler(callback));
    }
    Copy the code
  • The send method of the DartMessenger class is finally located as follows:
    @Override
    public void send(
          @NonNull String channel,
          @Nullable ByteBuffer message,
          @Nullable BinaryMessenger.BinaryReply callback) {
        Log.v(TAG, "Sending message with callback over channel '" + channel + "'");
        int replyId = 0;
        if (callback != null) {
          replyId = nextReplyId++;
          pendingReplies.put(replyId, callback);
        }
        if (message == null) {
          flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
        } else {
          flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId);
        }
    }
    Copy the code
  • A crash occurs when you try to send a message to a thread at once
    new Thread(new Runnable() {
        @Override
        public void run() {
            nativeChannel.invokeMethod("setNum", a , null);
        }
    }).start();
    Copy the code
  • The crash information is shown below
    java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: Thread-2574
        at io.flutter.embedding.engine.FlutterJNI.ensureRunningOnMainThread(FlutterJNI.java:992)
        at io.flutter.embedding.engine.FlutterJNI.dispatchPlatformMessage(FlutterJNI.java:736)
        at io.flutter.embedding.engine.dart.DartMessenger.send(DartMessenger.java:72)
        at io.flutter.embedding.engine.dart.DartExecutor$DefaultBinaryMessenger.send(DartExecutor.java:370)
        at io.flutter.plugin.common.MethodChannel.invokeMethod(MethodChannel.java:94)
        at com.ycbjie.ycandroid.channel.MethodChannelActivity.test1000(MethodChannelActivity.java:302)
        at com.ycbjie.ycandroid.channel.MethodChannelActivity.access$000(MethodChannelActivity.java:46)
        at com.ycbjie.ycandroid.channel.MethodChannelActivity$1.run(MethodChannelActivity.java:98)
        at java.lang.Thread.run(Thread.java:818)
    Copy the code

12.Flutter sends data to NA

  • From method. The invokeMethod (‘ android ‘, map); To analyze
    @optionalTypeArgs Future<T> _invokeMethod<T>(String method, { bool missingOk, dynamic arguments }) async { assert(method ! = null); final ByteData result = await binaryMessenger.send( name, codec.encodeMethodCall(MethodCall(method, arguments)), ); return codec.decodeEnvelope(result) as T; }Copy the code
  • Finally, navigate to the send method in the _DefaultBinaryMessenger class
      Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
        final Completer<ByteData> completer = Completer<ByteData>();
        // ui.window is accessed directly instead of using ServicesBinding.instance.window
        // because this method might be invoked before any binding is initialized.
        // This issue was reported in #27541. It is not ideal to statically access
        // ui.window because the Window may be dependency injected elsewhere with
        // a different instance. However, static access at this location seems to be
        // the least bad option.
        ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
          try {
            completer.complete(reply);
          } catch (exception, stack) {
            FlutterError.reportError(FlutterErrorDetails(
              exception: exception,
              stack: stack,
              library: 'services library',
              context: ErrorDescription('during a platform message response callback'),
            ));
          }
        });
        return completer.future;
      }
    Copy the code

13. Stability of Channel communication transmission

  • Does a channel lose data? We can simulate that Android sends 1000 messages to flutter, and then Flutter sends 1000 messages to Android. Here’s how to test this:

13.1 Android sends data to Flutter

  • Android sends data to Flutter with the code shown below
    int a = 0; private void test1000() { if (nativeChannel! =null){ for (int i=0 ; i<1000 ; i++){ a++; Log. I (" test1000: ", a+""); nativeChannel.invokeMethod("setNum", a , new MethodChannel.Result() { @SuppressLint("SetTextI18n") @Override public void success(@Nullable Object result) { if (result==null){ return; } log. I (" test data: ", result.tostring ()); } @SuppressLint("SetTextI18n") @Override public void error(String errorCode, @Nullable String errorMessage, @nullable Object errorDetails) {log. I (" error: ",errorMessage); } @Override public void notImplemented() { } }); }}}Copy the code
  • Flutter receives data and sends it back to Android
    / / accept na end passed way, and to respond to logical processing method. The setMethodCallHandler (nativeCallHandler); // Register method, Future< Dynamic > nativeCallHandler(MethodCall MethodCall) Async {switch (methodCall.method) {case "SetNum ": // get arguments int message = await methodcall.arguments; Print (" native Android passes ------ $message"); Return "flutter callback data: ${message.tostring ()}"; break; }}Copy the code

13.2 Viewing Data Stability and Timeliness

  • Android sends message logs
    The 2021-08-26 11:58:03. 837, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 990 the 2021-08-26 11:58:03. 837, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 991 the 2021-08-26 11:58:03. 837, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 992 the 2021-08-26 11:58:03. 837, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 993 the 2021-08-26 11:58:03. 838, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 994 the 2021-08-26 11:58:03. 838, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 995 the 2021-08-26 11:58:03. 838, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 996 the 2021-08-26 11:58:03. 838, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 997 the 2021-08-26 11:58:03. 838, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 998 the 2021-08-26 11:58:03. 838, 23106-23106 / com. Ycbjie. Test1000 ychybrid I/test data: : 999 the 2021-08-26 11:58:03. 838, 23106-23106 / com. Ycbjie. Ychybrid I/test data test1000: : 1000Copy the code
  • Flutter receives data sent by Android
    The 2021-08-26 11:52:39. 708, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: Native android passed parameters for the 2021-08-26 -- -- -- -- -- - 992 11:52:39. 709, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: Native android passed parameters for the 2021-08-26 -- -- -- -- -- - 993 11:52:39. 709, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: Native android passed parameters for the 2021-08-26 -- -- -- -- -- - 994 11:52:39. 709, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: Native android passed parameters for the 2021-08-26 -- -- -- -- -- - 995 11:52:39. 709, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: Native android passed parameters for the 2021-08-26 -- -- -- -- -- - 996 11:52:39. 710, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: Native android passed parameters for the 2021-08-26 -- -- -- -- -- - 997 11:52:39. 710, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: Native android passed parameters for the 2021-08-26 -- -- -- -- -- - 998 11:52:39. 710, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: Native android passed parameters for the 2021-08-26 -- -- -- -- -- - 999 11:52:39. 710, 23106-23627 / com. Ycbjie. Ychybrid I/flutter: The argument passed in by native Android is ------ 1000Copy the code
  • When Flutter receives the message, it calls back the data to Android. Android listens for callback data and prints the following log
    The 2021-08-26 11:58:03. 964, 23106-23106 / com. Ycbjie. Ychybrid I/test data: : Flutter callback data: 600 2021-08-26 11:58:03. 964, 23106-23106 / com. Ycbjie. Ychybrid I/test data: : Flutter callback data: 601 2021-08-26 11:58:03. 964, 23106-23106 / com. Ycbjie. Ychybrid I/test data: : Flutter callback data: 602 2021-08-26 11:58:03. 965, 23106-23106 / com. Ycbjie. Ychybrid I/test data: : Flutter callback data: 603 2021-08-26 11:58:03. 965, 23106-23106 / com. Ycbjie. Ychybrid I/test data: : Flutter callback data: 604 2021-08-26 11:58:03. 965, 23106-23106 / com. Ycbjie. Ychybrid I/test data: : Flutter callback data: 605 2021-08-26 11:58:03. 965, 23106-23106 / com. Ycbjie. Ychybrid I/test data: : Flutter callback data: 606 2021-08-26 11:58:03. 966, 23106-23106 / com. Ycbjie. Ychybrid I/test data: : flutter callback data: 607Copy the code
  • Then look at another wave of printed logs, as shown below
    The 2021-08-26 12:07:09. 158, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 1 2021-08-26 12:07:09. 237, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 1 2021-08-26 12:07:09. 240, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 2 2021-08-26 12:07:09. 241, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 3 2021-08-26 12:07:09. 241, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 2 2021-08-26 12:07:09. 241, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 3 2021-08-26 12:07:09. 241, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 4 2021-08-26 12:07:09. 241, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 4 2021-08-26 12:07:09. 241, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 5 2021-08-26 12:07:09. 241, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 5 2021-08-26 12:07:09. 242, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 6 2021-08-26 12:07:09. 242, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 6 2021-08-26 12:07:09. 242, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 7 2021-08-26 12:07:09. 242, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 7 2021-08-26 12:07:09. 272, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 131 2021-08-26 12:07:09. 273, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 131 2021-08-26 12:07:09. 273, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 132 2021-08-26 12:07:09. 273, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 132 2021-08-26 12:07:09. 273, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 133 2021-08-26 12:07:09. 273, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 133 2021-08-26 12:07:09. 273, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 134 2021-08-26 12:07:09. 273, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 134 2021-08-26 12:07:09. 273, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 135 2021-08-26 12:07:09. 274, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 135 2021-08-26 12:07:09. 274, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 136 2021-08-26 12:07:09. 274, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : Flutter callback data: 136 2021-08-26 12:07:09. 274, 24237-24990 / com. Ycbjie. Ychybrid I/flutter: Test data and the flutter receive NA data: 137 2021-08-26 12:07:09. 274, 24237-24237 / com. Ycbjie. Ychybrid I/test data, NA receive flutter callback: : flutter callback data: 137Copy the code
  • Therefore, the logs show that data transmission ensures the timeliness of data, and there is a one-to-one correspondence between sending and receiving messages. There are no failures, so the transfer of data is stable.
  • If you pass the int value 1000 times, you may pass a large json value. If you pass the int value 1000 times, you may pass a large JSON value.

14. How to implement onActivityResult

  • Let me start with a scene
    • It is common in development to close the current page while returning data from the previous page, which is implemented in Android via startActivityForResult and onActivityResult().
    • Instead of adding parameters to the navigator.of (context).pop() method, you can use MethodChannel to transfer data between a Flutter page and an Android native page when returning to the previous page.

14.1 Flutter Page Return to the Android native page

  • Call the native return method on the Flutter side. First, add a button to the Flutter page. Click the button to return to the native page as follows:
    New Padding(Padding: const edgeinsets. only(left: 10.0, top: 10.0, Right: 10.0), Child: new RaisedButton(textColor: Color.black, child: new Text(' return to previous screen and carry data '), onPressed: () {Map<String, dynamic> Map = {'message': 'I came back from the Flutter page '}; String result = await method.invokeMethod('goBackWithResult', map); })),Copy the code
  • The Android side still executes the specified code by judging the value of methodCall.method and retrieving the arguments passed by Flutter via methodCall.argument().
    nativeChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(@NonNull MethodCall methodCall, @nonnull methodChannel.result Result) {if ("goBackWithResult".equals(methodCall.method)) {// return the previous page, Intent backIntent = new Intent(); backIntent.putExtra("message", (String) methodCall.argument("message")); setResult(RESULT_OK, backIntent); finish(); }}});Copy the code

14.2 Android Native Page Return to the Flutter page

  • The Android native page returns to the Flutter page
    • This situation requires native to call the Flutter code in the same way that a Flutter calls native methods. First trigger the flutter page button to jump to the NA page from the flutter, then trigger the NA page return operation to return to the flutter page and transfer data.
  • First, the flutter page triggers the code operation logic to jump to the NA page, as shown below
    // Flutter new Padding(Padding: const EdgeInsets. Only (left: 10.0, top: 10.0, right: 10.0), child: New RaisedButton(textColor: colors. black, child: new Text(' switch to native ', echo result: $_methodResult1'), onPressed: () { _jumpToNative(); }),), //na, note that na receives the flutter command, Na is a new page calls startActivityForResult operation jump to na nativeChannel. SetMethodCallHandler (new MethodChannel. MethodCallHandler () { @Override public void onMethodCall(@NonNull MethodCall methodCall, @nonnull methodChannel.result Result) {if ("doubi".equals(methodCall.method)) {// Receives instructions from a flutter // jumps to the specified Activity Intent  intent = new Intent(MethodChannelActivity.this, MethodResultActivity.class); startActivityForResult(intent,RESULT_OK2); Result.success ("Na received instruction "); // Return the argument to flutter result.success("Na received instruction "); }}});Copy the code
  • Then the next step is to return from NA to the flutter page and call the flutter method again. The specific operation code is as follows
    Public class MethodResultActivity extends AppCompatActivity {@suppressLint ("SetTextI18n") @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_android); TextView tv = findViewById(R.id.tv); Tv.settext (" Flutter page opens NA page, test Android native page to return flutter page "); tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(); Intent. PutExtra ("message", "I'm back from the native page "); setResult(RESULT_OK2, intent); finish(); }}); Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data ! = null && resultCode==RESULT_OK2) {// MethodResultActivity returns data String message = data.getStringExtra("message"); Map<String, Object> result = new HashMap<>(); result.put("message", message); / / call the method of defining the Flutter end nativeChannel. InvokeMethod (" onActivityResult ", the result, new MethodChannel.Result() { @SuppressLint("SetTextI18n") @Override public void success(@Nullable Object result) { Tvcontent.settext (" test content 2: "+result); } @SuppressLint("SetTextI18n") @Override public void error(String errorCode, @Nullable String errorMessage, @nullable Object errorDetails) {tvContent.settext (" test content: flutter passed to NA error 2"); } @Override public void notImplemented() { } }); } } //flutter Future<dynamic> handler(MethodCall call) async { switch (call.method) { case 'onActivityResult': Print (call.arguments['message']); Return "Hello, this is data from flutter "; } } flutterChannel.setMethodCallHandler(handler);Copy the code

recommended

  • Fluter Utils utility library: github.com/yangchong21…
  • The Flutter hybrid project code example: github.com/yangchong21…