关于flutter:Flutter-Chanel通信流程

112次阅读

共计 27507 个字符,预计需要花费 69 分钟才能阅读完成。

目录介绍

  • 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…

正文完
 0