关于flutter:Flutter-Chanel通信流程

目录介绍

  • 01.flutter和原生之间交互
  • 02.MethodChanel流程
  • 03.MethodChanel应用流程
  • 04.MethodChanel代码实际
  • 05.EventChannel流程
  • 06.EventChannel根本流程
  • 07.EventChannel代码实现
  • 08.BasicMessageChannel流程
  • 09.BasicMessageChannel根本流程
  • 10.BasicMessageChannel代码实现
  • 11.Channel编解码器阐明
  • 12.Channel通信能够子线程吗
  • 13.Channel通信传递稳定性
  • 14.onActivityResult如何实现

举荐

  • fluter Utils 工具类库:https://github.com/yangchong2…
  • flutter 混合我的项目代码案例:https://github.com/yangchong2…

01.flutter和原生之间交互

1.1 交互简略介绍

  • 官网给的通信形式

    • 看图片,channel通信形式
    • 从底层来看,Flutter和平台端通信的形式是发送异步的二进制音讯,该根底通信形式在Flutter端由BinaryMessages来实现, 而在Android端是一个接口BinaryMessenger,其具体实现为FlutterNativeView,在iOS端是一个协定 FlutterBinaryMessenger,FlutterViewController恪守并实现了这个协定。
  • flutter能够与native之间进行通信,帮忙咱们应用native提供的能力。

    • 通信是双向的,咱们能够从Native层调用flutter层的dart代码,同时也能够从flutter层调用Native的代码。
  • 咱们须要应用Platform Channels APIs进行通信,次要包含上面三种:

    • MethodChannel:用于传递办法调用(method invocation)
    • EventChannel:用于事件流的发送(event streams)
    • BasicMessageChannel:用于传递字符串和半结构化的音讯,这里什么叫做半结构化?上面会解释……
  • channel通信是异步还是同步的

    • 为了保障用户界面在交互过程中的流畅性,无论是从Flutter向Native端发送音讯,还是Native向Flutter发送音讯都是以异步的模式进行传递的。那为何不应用同步来操作,上面会说到……
  • 几种channel利用场景剖析

    • MethodChannel应用场景:无论是Flutter端还是Native端都能够通过MethodChannel向对方平台发送两端提前定义好的办法名来调用对方平台绝对应的音讯解决逻辑并且带回返回值给被调用方。
    • EventChannel的应用场景:更侧重于Native平台被动向Flutter平台,单向给Flutter平台发送音讯,Flutter无奈返回任何数据给Native端,EventChannel形容是单通的。能够类比Android外面的播送……
    • BasicMessageChannel的应用场景:比方flutter想拍照,拍完照后的图片门路须要传给flutter,照片的门路发送能够应用BasicMessageChannel.Reply回复,也能够应用sendMessage被动再发一次音讯。集体认为接管音讯并回复音讯属于一次通信,所以偏向于应用BasicMessageChannel.Reply。
  • 混合开发通常用那种channel

    • 只是混合开发通常波及到两端频繁通信,集体更加偏向应用BasicMessageChannel,不分主客,应用和通信更不便。

1.2 外围类重点阐明

  • MethodCall

    • 办法调用Java层封装,次要是数据类
  • MethodChannel

    • 这个次要用户和dart进行办法通信,类
  • MethodCallHandler

    • 这个java层解决dart层工夫的接口,在通信协定中属于下层接口,接口
  • BinaryMessageHandler

    • java层和dart层通信的最底层形象接口,面向二进制数据包,接口
  • DartMessenger

    • 最底层用于接管JNI发送过去的数据。实现类
  • DartExecutor

    • 配置、疏导并开始执行Dart代码。BinaryMessenger的具体实现类
  • FlutterView

    • NA用来承载flutter的容器view
  • IncomingMethodCallHandler

    • BinaryMessageHandler的实现类,用户接管底层发送过去的数据包,而后转发给MethodCallHandler,并对MethodCallHandler 发送过的后果进行打包发送给dart层。实现类
  • FlutterJNI

    • JNI层的封装用于跟底层引擎侧进行通信

02.MethodChannel流程

  • 其中最罕用的是MethodChanel,MethodChanel的应用与在Android的JNI调用十分相似,然而MethodChanel更加简略,而且绝对于JNI的同步调用MethodChanel的调用是异步的:

  • 从flutter架构图上能够看到,flutter与native的通信产生在Framework和Engine之间,framewrok外部会将MethodChannel以BinaryMessage的模式与Engine进行数据交换。

03.MethodChanel应用流程

3.1 flutter调用native

  • flutter调用native步骤

    • [native] 应用MethodChannel#setMethodCallHandler注册回调
    • [flutter] 通过MethodChannel#invokeMethod发动异步调用
    • [native] 调用native办法通过Result#success返回Result,出错时返回error
    • [flutter] 收到native返回的Result
  • 如图所示

3.2 native调用flutter

  • native调用flutter

    • 与flutter调用native的程序完全一致,只是[native]与[flutter]角色反调
  • 如图所示

  • NA端应用MethodChannel

    • 首先定义Channel名称,须要保障是惟一的,在Flutter端须要应用同样的名称来创立MethodChannel。如果名称不一样,则会导致匹配不上……
    • 第一个参数:是messenger,类型是BinaryMessenger,是一个接口,代表音讯信使,是音讯发送与接管的工具;
    • 第二个参数:是name,就是Channel名称,和flutter定义的要一样;
    • 第三个参数:是codec,类型是MethodCodec,代表音讯的编解码器,如果没有传该参数,默认应用StandardMethodCodec。

04.MethodChanel代码实际

4.1 native调用flutter

  • 定义好了MethodChannel之后调用setMethodCallHandler()办法设置音讯解决回调,参数是MethodHandler类型,须要实现它的onMethodCall()办法。onMethodCall()办法有两个参数methodCall和result,methodCall记录了调用的办法信息,包含办法名和参数,result用于办法的返回值,能够通过result.success()办法返回信息给Flutter端。

    private void createChannel() {
        nativeChannel = new MethodChannel(binaryMessenger, METHOD_CHANNEL, StandardMethodCodec.INSTANCE);
        // 注册Handler实现
        nativeChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
                if ("android".equals(methodCall.method)) {
                    //接管来自flutter的指令
                    String flutter = methodCall.argument("flutter");
                    //返回给flutter的参数
                    result.success("Na收到指令");
                } 
            }
        });
    }
  • 能够通过invokeMethod办法让NA执行调用flutter办法。那么执行了flutter办法后须要回传数据,这个时候就须要用到Result接口呢,代码如下所示:

    HashMap<String , String> map = new HashMap<>();
    map.put("invokeKey","你好,这个是从NA传递过去的数据");
    //nativeChannel.resizeChannelBuffer(100);
    nativeChannel.invokeMethod("getFlutterResult", map , new MethodChannel.Result() {
        @SuppressLint("SetTextI18n")
        @Override
        public void success(@Nullable Object result) {
            tvContent.setText("测试内容:"+result);
        }
    
        @SuppressLint("SetTextI18n")
        @Override
        public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
            tvContent.setText("测试内容:flutter传递给na数据传递谬误");
        }
    
        @Override
        public void notImplemented() {
    
        }
    });
  • 事件接管解决端

    • 接管解决回调时onMethodCall(MethodCall call, MethodChannel.Result result)通过methodCall接管事件发送者传递回来的信息,通过Result把解决完的后果发送给事件发送方。
    • 通过methodCall.method:来辨别不同函数名(办法)名以执行不同的业务逻辑,
    • 通过methodCall.hasArgument(”key”):判断是否有某个key对应的value
    • 通过methodCall.argument(”key”):获取key对应的value值
    • 通过result.success(object):把解决完的后果返回给事件发送方
  • 事件发送端

    • 处理事件发送方通过methodChannel.invokeMethod(“办法名”,”要传递的参数”)把须要传递的参数传递给事件监听者。 其中
    • 办法名:不能为空
    • 要传递的参数:能够为空,若不为空则必须为可Json序列化的对象。
    • callback:能够为空,若不为空则示意执行了flutter办法后的回调监听状态

4.2 flutter调用native

  • Flutter应用MethodChannel

    • 在Flutter端同样须要定义一个MethodChannel,应用MethodChannel须要引入services.dart包,Channel名称要和Android端定义的雷同。

      static const method = const MethodChannel('com.ycbjie.android/method');
  • 增加监听NA调用flutter办法的监听,flutter代码是setMethodCallHandler办法实现。return则示意flutter回传给NA的数据操作。

      method.setMethodCallHandler(nativeCallHandler);
    
      // 注册办法,期待被原生通过invokeMethod唤起
      Future<dynamic> nativeCallHandler(MethodCall methodCall) async {
        switch (methodCall.method) {
          case "getFlutterResult":
          //获取参数
            String paramsFromNative = await methodCall.arguments["invokeKey"];
            print("原生android传递过去的参数为------ $paramsFromNative");
            return "你好,这个是从flutter回传给NA的数据";
            break;
        }
      }
  • flutter是如何给NA发送音讯的呢,间接调用invokeMethod办法,代码如下所示

      Future<Null> _jumpToNativeWithParams1() async {
        Map<String, String> map = { "flutter": "这是一条来自flutter的参数" };
        String result = await method.invokeMethod('android', map);
        print(result);
      }

05.EventChannel流程

  • EventChannel用于从native向flutter发送告诉事件,例如flutter通过其监听Android的重力感应变动等。与MethodChannel不同,EventChannel是native到flutter的单向调用,调用是多播(一对多)的,能够类比成Android的Brodecast播送。

06.EventChannel根本流程

  • 照例先看一下API应用的根本流程:

    • [native]EventChannel#setStreamHandler注册Handler实现
    • [native]EventChannel初始化完结后,在StreamHandler#onLister回调中获取EventSink援用并保留
    • [flutter]EventChannel#receiveBroadcastStream注册listener,建设监听
    • [native]应用EventSink#sucess发送告诉事件
    • [flutter]承受到事件告诉
    • [native]告诉完结时调用endOfStream完结
  • 如图所示

07.EventChannel代码实现

  • flutter端

    • 创立EventChannel,注册“包名/标识符”的channel名
    • 通过StreamSubscription#listen注册listener,其中cancelOnError参数示意遇到谬误时是否主动完结监听

      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();
      }
  • native(android)端

    • 通过EventChannel#setStreamHandler注册Handler实现
    • 初始化实现后,获取eventSink援用并保留
    • eventSink发送事件告诉
    • 告诉完结时调用event#endOfStream,此时onCancel会被调用
    • 必要时,可通过evnetSink#error发送谬误告诉,flutter的StreamSubscription#onError会收到告诉

      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")
                      }
                  })
      }
      }

08.BasicMessageChannel流程

  • BasicMessageChannel用于在flutter和native相互发送音讯,一方给另一方发送音讯,收到音讯之后给出回复。

09.BasicMessageChannel根本流程

  • flutter向native发送音讯

    • [flutter]创立BasicMessageChannel
    • [native]通过BasicMessageChannel#MessageHandler注册Handler
    • [flutter]通过BasicMessageChannel#send发送音讯
    • [native]BasicMessageChannel#MessageHandler#onMessage中接管音讯,而后reply
  • 如图所示

  • native向flutter发送音讯

    • 流程也是一样的,只是将[flutter]与[native]反调
  • 如图所示

10.BasicMessageChannel代码实现

10.1flutter端

  • flutter须要实现以下工作

    • 创立BasicMessageChannel
    • 通过BasicMessageChannel#send发送音讯
  • 绝对与其余Channel类型的创立,MessageChannel的创立除了channel名以外,还须要指定编码方式:

    BasicMessageChannel(String name, MessageCodec<T> codec, {BinaryMessenger binaryMessenger})
  • 发送的音讯会以二进制的模式进行解决,所以要针对不同类型的数进行二进制编码

    • 编码类型 音讯格局
    • BinaryCodec 发送二进制音讯时
    • JSONMessageCodec 发送Json格局音讯时
    • StandardMessageCodec 发送基本型数据时
    • StringCodec 发送String类型音讯时
  • 代码

    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();
      }

10.2 native(android)端

  • android端实现以下工作:

    • 创立BasicMessageChannel
    • 通过setHandler注册MessageHandler
    • MessageHandler#onMessage回调中接管到message后,通过reply进行回复
  • 代码

    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)
        }
    }

11.Channel编解码器阐明

11.1 什么是音讯编解码器

  • 什么是音讯编解码器

    • 在Flutter和平台间进行互相通信了,然而收发的数据都是二进制的,这就须要开发者思考更多的细节,如字节程序(大小端)和怎么示意更高级的音讯类型,如字符串,map等。
    • 因而,Flutter 还提供了音讯编解码器(Codec), 用于高级数据类型(字符串,map等)和二进制数据(byte)之间的转换,即音讯的序列化和反序列化。
  • 音讯编解码器品种有哪些

    • MethodCodec:办法传递的编解码器形象,接口
    • JSONMethodCodec:MethodCodec的实现类,会把数据打包成json构造发送给dart,类
    • StandardMethodCodec:MethodCodec的实现类,会把数据打包成默认格局发送给dart,类

11.2 四种音讯编解码器类型

  • BinaryCodec

    • MessageCodec的实现类,间接发送二进制数据
    • BinaryCodec是最为简略的一种Codec,因为其返回值类型和入参的类型雷同,均为二进制格局(Android中为ByteBuffer,iOS中为NSData)。实际上,BinaryCodec在编解码过程中什么都没做,只是一成不变将二进制数据音讯返回而已。或者你会因而感觉BinaryCodec没有意义,然而在某些状况下它十分有用,比方应用BinaryCodec能够使传递内存数据块时在编解码阶段免于内存拷贝。
  • StringCodec

    • MessageCodec的实现类,负责解码和编码String类型的音讯
    • 应用 UTF-8 编码格局对字符串数据进行编解码,在Android平台转换为 java.util.String 类型
  • JSONMessageCodec

    • MessageCodec的实现类,负责解码和编码Json类型的音讯
    • JSONMessageCodec用于解决 JSON 数据类型(字符串型,数字型,布尔型,null,只蕴含这些类型的数组,和key为string类型,value为这些类型的map),在编码过程中,数据会被转换为JSON字符串,而后在应用 UTF-8 格局转换为字节型。
  • StandardMessageCodec

    • MessageCodec的实现类,负责解码和编码默认类型的音讯
    • StandardMessageCodec 能够认为是 JSONMessageCodec 的升级版,可能解决的数据类型要比 JSONMessageCodec 更广泛一些,且在解决 int 型数据时,会依据 int 数据的大小来转为平台端的32位类型(int)或者是64位类型(long),StandardMessageCodec 也是 Flutter Platform channel 的默认编解码器

11.3 编码器的源码剖析下

  • 首先看下MessageCodec

    abstract class MessageCodec<T> {
    
      ByteData encodeMessage(T message);
    
      T decodeMessage(ByteData message);
    }

11.4 看StandardMessageCodec

  • StandardMessageCodec略微简单

    • StandardMessageCodec在写入数据的时候,显示写入这个数据的类型值定义,而后在写入其对应的具体值,什么意思呢?
  • 查看一下如何写入指定类型的值,代码如下所示:

      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);
        }
      }
  • 查看一下如何读取指定类型的值,代码如下所示:

      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;
      }

11.5 如何抉择适合编解码器

  • 编解码的实现类并不简单

    • 能够先理解一下这个比拟能更好的了解数据传递,其实不关java下层应用那种形式,最终传递给底层数据都是固定格局,约定对立的数据格式单方能力辨认进去,失常的来说用默认的编解码格局就能够了。
  • 对于四种解码器应用场景

    • BinaryCodec

      • 暂未找到应用的场景
    • StringCodec

      • 实用发送繁多的字符串数据,数据量繁多的状况,比方LifecycleChannel

        public void appIsInactive() {
        Log.v(TAG, "Sending AppLifecycleState.inactive message.");
        channel.send("AppLifecycleState.inactive");
        }
    • JSONMessageCodec

      • 实用数据量比较复杂的状况,比方有携带多个数据字段的传递,比方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);
        }
    • StandardMessageCodec

      • 默认的数据编解码,绝大多数的状况下应用默认的就能够了。比方:MethodChannel,EventChannel

12.Channel通信能够子线程吗

12.1 Android发送通信信息

  • 首先看一下Android发送通信信息,次要剖析入口是: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));
    }
  • 最终定位找到DartMessenger类的send办法,代码如下所示:

    @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);
        }
    }
  • 尝试一下子线程发送音讯,发现会呈现解体

    new Thread(new Runnable() {
        @Override
        public void run() {
            nativeChannel.invokeMethod("setNum", a , null);
        }
    }).start();
  • 解体信息如下所示

    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)

12.Flutter给NA发送数据

  • 从method.invokeMethod(‘android’, map);开始剖析

      @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;
      }
  • 最初定位到_DefaultBinaryMessenger类中的send办法

      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;
      }

13.Channel通信传递稳定性

  • channel传递数据是否会失落,如何测试呢?能够模仿,Android给flutter发送1000条信息,而后flutter给Android发送1000条信息,接下来看一下如何测试:

13.1 Android给flutter发送数据

  • Android给flutter发送数据,代码如下所示

    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("测试数据:",result.toString());
                    }
    
                    @SuppressLint("SetTextI18n")
                    @Override
                    public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
                        Log.i("测试数据异样:",errorMessage);
                    }
    
                    @Override
                    public void notImplemented() {
    
                    }
                });
            }
        }
    }
  • flutter接收数据并回传数据给Android

      //承受na端传递过去的办法,并做出响应逻辑解决
      method.setMethodCallHandler(nativeCallHandler);
    
      // 注册办法,期待被原生通过invokeMethod唤起
      Future<dynamic> nativeCallHandler(MethodCall methodCall) async {
        switch (methodCall.method) {
          case "setNum":
          //获取参数
            int message = await methodCall.arguments;
            print("原生android传递过去的参数为------ $message");
            return "flutter回调数据:${message.toString()}";
            break;
        }
      }

13.2 查看数据稳定性和及时性

  • Android发送消息日志

    2021-08-26 11:58:03.837 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 990
    2021-08-26 11:58:03.837 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 991
    2021-08-26 11:58:03.837 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 992
    2021-08-26 11:58:03.837 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 993
    2021-08-26 11:58:03.838 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 994
    2021-08-26 11:58:03.838 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 995
    2021-08-26 11:58:03.838 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 996
    2021-08-26 11:58:03.838 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 997
    2021-08-26 11:58:03.838 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 998
    2021-08-26 11:58:03.838 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 999
    2021-08-26 11:58:03.838 23106-23106/com.ycbjie.ychybrid I/测试数据test1000:: 1000
  • flutter接管Android发送数据

    2021-08-26 11:52:39.708 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 992
    2021-08-26 11:52:39.709 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 993
    2021-08-26 11:52:39.709 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 994
    2021-08-26 11:52:39.709 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 995
    2021-08-26 11:52:39.709 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 996
    2021-08-26 11:52:39.710 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 997
    2021-08-26 11:52:39.710 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 998
    2021-08-26 11:52:39.710 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 999
    2021-08-26 11:52:39.710 23106-23627/com.ycbjie.ychybrid I/flutter: 原生android传递过去的参数为------ 1000
  • flutter收到音讯后,回调给Android数据。Android监听回调数据,打印日志如下

    2021-08-26 11:58:03.964 23106-23106/com.ycbjie.ychybrid I/测试数据:: flutter回调数据:600
    2021-08-26 11:58:03.964 23106-23106/com.ycbjie.ychybrid I/测试数据:: flutter回调数据:601
    2021-08-26 11:58:03.964 23106-23106/com.ycbjie.ychybrid I/测试数据:: flutter回调数据:602
    2021-08-26 11:58:03.965 23106-23106/com.ycbjie.ychybrid I/测试数据:: flutter回调数据:603
    2021-08-26 11:58:03.965 23106-23106/com.ycbjie.ychybrid I/测试数据:: flutter回调数据:604
    2021-08-26 11:58:03.965 23106-23106/com.ycbjie.ychybrid I/测试数据:: flutter回调数据:605
    2021-08-26 11:58:03.965 23106-23106/com.ycbjie.ychybrid I/测试数据:: flutter回调数据:606
    2021-08-26 11:58:03.966 23106-23106/com.ycbjie.ychybrid I/测试数据:: flutter回调数据:607
  • 而后再看一波打印日志,如下所示

    2021-08-26 12:07:09.158 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 1
    2021-08-26 12:07:09.237 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:1
    2021-08-26 12:07:09.240 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 2
    2021-08-26 12:07:09.241 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 3
    2021-08-26 12:07:09.241 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:2
    2021-08-26 12:07:09.241 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:3
    2021-08-26 12:07:09.241 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 4
    2021-08-26 12:07:09.241 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:4
    2021-08-26 12:07:09.241 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 5
    2021-08-26 12:07:09.241 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:5
    2021-08-26 12:07:09.242 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 6
    2021-08-26 12:07:09.242 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:6
    2021-08-26 12:07:09.242 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 7
    2021-08-26 12:07:09.242 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:7
    
    2021-08-26 12:07:09.272 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 131
    2021-08-26 12:07:09.273 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:131
    2021-08-26 12:07:09.273 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 132
    2021-08-26 12:07:09.273 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:132
    2021-08-26 12:07:09.273 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 133
    2021-08-26 12:07:09.273 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:133
    2021-08-26 12:07:09.273 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 134
    2021-08-26 12:07:09.273 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:134
    2021-08-26 12:07:09.273 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 135
    2021-08-26 12:07:09.274 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:135
    2021-08-26 12:07:09.274 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 136
    2021-08-26 12:07:09.274 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:136
    2021-08-26 12:07:09.274 24237-24990/com.ycbjie.ychybrid I/flutter: 测试数据,flutter接管NA数据: 137
    2021-08-26 12:07:09.274 24237-24237/com.ycbjie.ychybrid I/测试数据,NA接管flutter回调:: flutter回调数据:137
  • 因而查看日志能够得悉,传递数据保障了数据的时效性,发送音讯和接管音讯是一一对应。并没有失败的状况,因而传递数据是稳固的。
  • 重点阐明,有小伙伴有纳闷,你这遍历1000次,每次传递都是int值,那理论开发中可能传递大json,数据量大的状况会怎么,这个上面会说到……

14.onActivityResult如何实现

  • 先说一个场景

    • 在开发中咱们常常会遇到敞开以后页面的同时返回给上一个页面数据的场景,在Android中是通过startActivityForResult和onActivityResult()实现的。
    • 而纯Flutter页面之间能够通过在Navigator.of(context).pop()办法中增加参数来实现,那么对于Flutter页面和Android原生页面之间如何在返回上一页时传递数据呢,通过MethodChannel就能够实现。

14.1 Flutter页面返回Android原生页面

  • 在Flutter端调用原生的返回办法就能够了,首先在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('返回上一界面,并携带数据'),
            onPressed: () {
                Map<String, dynamic> map = {'message': '我从Flutter页面回来了'};
                String result = await method.invokeMethod('goBackWithResult', map);
            }),
      ),
  • Android端仍然是通过判断methodCall.method的值来执行指定的代码,通过methodCall.argument()获取Flutter传递的参数。

    nativeChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
        @Override
        public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
            if ("goBackWithResult".equals(methodCall.method)) {
                // 返回上一页,携带数据
                Intent backIntent = new Intent();
                backIntent.putExtra("message", (String) methodCall.argument("message"));
                setResult(RESULT_OK, backIntent);
                finish();
            }
        }
    });

14.2 Android原生页面返回Flutter页面

  • Android原生页面返回Flutter页面

    • 这种状况须要原生来调用Flutter代码,和Flutter调用原生办法的步骤是一样的。首先触发flutter页面按钮,从flutter跳转na页面,而后触发na页面返回操作,返回到Flutter页面,并传递数据。
  • 首先是flutter页面触发跳转到na页面的代码操作逻辑,代码如下所示

    //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('跳转到原生逗比界面,回调后果:$_methodResult1'),
            onPressed: () {
              _jumpToNative();
            }),
      ),
    
    //na,留神na接管到flutter指令后,na是调用startActivityForResult操作跳转到na的新页面
    nativeChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
        @Override
        public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
            if ("doubi".equals(methodCall.method)) {
                //接管来自flutter的指令
                //跳转到指定Activity
                Intent intent = new Intent(MethodChannelActivity.this, MethodResultActivity.class);
                startActivityForResult(intent,RESULT_OK2);
                //返回给flutter的参数
                result.success("Na收到指令");
            }
        }
    });
  • 而后接下来的一步是,从NA返回到flutter页面,而后再去调用flutter办法。具体操作代码如下所示

    //na flutter触发关上na的新的页面
    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页面关上NA页面,测试Android原生页面返回Flutter页面");
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Intent intent = new Intent();
                    intent.putExtra("message", "我从原生页面回来了");
                    setResult(RESULT_OK2, intent);
                    finish();
                }
            });
        }
    }
    
    // na flutter承载容器的na的原生页面
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (data != null && resultCode==RESULT_OK2) {
            // MethodResultActivity返回的数据
            String message = data.getStringExtra("message");
            Map<String, Object> result = new HashMap<>();
            result.put("message", message);
            // 调用Flutter端定义的办法
            nativeChannel.invokeMethod("onActivityResult", result, new MethodChannel.Result() {
                @SuppressLint("SetTextI18n")
                @Override
                public void success(@Nullable Object result) {
                    tvContent.setText("测试内容2:"+result);
                }
    
                @SuppressLint("SetTextI18n")
                @Override
                public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
                    tvContent.setText("测试内容:flutter传递给na数据传递谬误2");
                }
    
                @Override
                public void notImplemented() {
    
                }
            });
        }
    }
    
    //flutter
      Future<dynamic> handler(MethodCall call) async {
        switch (call.method) {
          case 'onActivityResult':
            // 获取原生页面传递的参数
            print(call.arguments['message']);
            return "你好,这个是从flutter传递过去的数据";
        }
      }
    
      flutterChannel.setMethodCallHandler(handler);

举荐

  • fluter Utils 工具类库:https://github.com/yangchong2…
  • flutter 混合我的项目代码案例:https://github.com/yangchong2…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理