【Flutter】FlutterChannel详解

1 FlutterChannel原理

Flutter定义了三种不同类型的Channel,分别用于传递字符串和半结构化信息的BasicMessageChannel、用于传递方法调用(method invocation)的MethodChannel以及用于数据流(event streams)通信的EventChannel,如图所示:
在这里插入图片描述

三种Channel之间相互独立,各有用途,但在设计上非常相似。每种Channel 均有三个重要的成员变量:String 类型的 name,代表 Channel 的名字,也是其唯一的标识符;BinaryMessenger 类型的 messager,代表消息信使,是消息的发送与接收的工具;MessageCodec 类型或 MethodCodec 类型的 Codec,代表消息的编解码器。

它们的通信工具都是BinaryMessagerBinaryMessengerPlatform端与Flutter端通信的工具,其通信使用的消息格式为二进制格式数据。当初始化一个 Channel,并向该 Channel注册处理消息的 Handler 时,实际上会生成一个与之对应的 BinaryMessageHandler,并 以 channel name Key,注册到 BinaryMessenger 中。 当Flutter端将消息发送到BinaryMessenger时,BinaryMessenger会根据其入参Channel找到对应的BinaryMessageHandler,并交由其处理。

BinaryMessenger 并不知道 Channel 的 存 在, 它 只 和 BinaryMessageHandler 打交 道。 而 Channel BinaryMessageHandler 则 是 一 一 对 应 的。由 于 Channel BinaryMessageHandler 接收到的消息是二进制格式数据,无法直接使用,故 Channel 会将该二进制消息通过 Codec(消息编解码器)解码为能识别的消息,并传递给 Handler 处理。

Handler 处理完消息之后,会通过回调函数返回 result,并将 result 通过编解码器编码为二进制格式数据,通过 BinaryMessenger 发送回 Flutter 端。

另外,在使用 Platform Channel 时,还需要牢记以下两点:

  • Platform 侧的代码运行在主线程Flutter Engine 自己不创建线程,其线程的创建与管理是由 Embedder 提供的,并且 Flutter Engine 要求 Embedder 提供四个 Task Runner,分别是 Platform Task RunnerUI Task RunnerGPU Task RunnerI/O Task RunnerPlatform 侧执行的代码运行在 Platform Task Runner 中, 而在Flutter App 侧的代码运行在 UI Task Runner 中。在 Android 端和 iOS 端上,Platform Task Runner 运行在主线程上。因此,不应该在 Platform 端的 Handler 中处理耗时操作
  • Platform Channel 并非是线程安全的。这一点在 Flutter 官方文档中也有提及。FlutterEngine 中的多个组件是非线程安全的,故与 FlutterEngine 的所有交互(接口调用)必须发生在 Platform Thread。故在将 Platform 端的消息处理结果回传到Flutter 端时,需要确保回调函数是在 Platform Thread(也就是 AndroidiOS 的主线程)中执行的。

2 FlutterChannel使用

2.1 BasicMessageChannel使用

BasicMessageChannel主要用于传递字符串和半结构化信息。如果需要监听消息,则调用setMessageHandler,如果要发送消息,则调用send

2.1.1 Flutter发送消息给原生

  • 在FlutterActivity中创建BasicMessageChannel对象,并通过该对象与Flutter侧交互
class MyFlutterActivity : FlutterActivity() {
    
    

    companion object{
    
    
        fun startActivity(activity: Activity){
    
    
            val intent = Intent(activity,MyFlutterActivity::class.java)
            activity.startActivity(intent)
        }
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    
    
        val channel = BasicMessageChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "myflutter/testbasicmessagechannel",
            JSONMessageCodec.INSTANCE
        )

        channel.setMessageHandler {
    
     message, reply ->
            Log.d("MessageChannelTest", "in android Received message = $message")
            reply.reply("Reply from Android")
        }
    }

}
  • 在Flutter侧创建BasicMessageChannel对象,并通过该对象调用send向原生发送数据,并通过它的返回值获取native原生那边返回的数据。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class BasicMessageChannelPage extends StatefulWidget{
    
    
  @override
  State<StatefulWidget> createState() => _BasicMessageChannelPage();

}

class _BasicMessageChannelPage extends State<BasicMessageChannelPage>{
    
    

  static const _channel = BasicMessageChannel("myflutter/testbasicmessagechannel", JSONMessageCodec());
  int i = 0;
  
  void _sendMessage() async{
    
    
    final String reply = await _channel.send('hello world $i') as String;
    print('MessageChannelTest in dart $reply');
    setState(() {
    
    
      i++;
    });
  }

  @override
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        title: Text("BasicMessageChannelPage"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("You have pushed the button this many times:"),
            Text(
              '$i',
              style: Theme.of(context).textTheme.headline4,
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed:_sendMessage,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), //
    );
  }

}

运行结果:
在这里插入图片描述

2.1.1 原生发送消息给Flutter

原生端代码:

class MyFlutterActivity : FlutterActivity() {
    
    

    companion object{
    
    
        fun startActivity(activity: Activity){
    
    
            val intent = Intent(activity,MyFlutterActivity::class.java)
            activity.startActivity(intent)
        }
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    
    
        val channel = BasicMessageChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "myflutter/testbasicmessagechannel",
            JSONMessageCodec.INSTANCE
        )
        //发送数据
        mChannel.send("dddddd");
    }

}

Flutter端

static const _channel = BasicMessageChannel("myflutterfragment/searchscandata", StandardMessageCodec());

	_channel.setMessageHandler((message){
    
    
      print('$message')
      return Future(() => "");
    });

2.2 MethodChannel使用

2.2.1 Flutter调用Native方法

  • 在MyFlutterActivity中创建一个MethodChannel对象,然后通过setMethodCallHandler来处理flutter端调用请求。
class MyFlutterActivity : FlutterActivity() {
    
    

    companion object {
    
    
        fun startActivity(activity: Activity) {
    
    
            val intent = Intent(activity, MyFlutterActivity::class.java)
            activity.startActivity(intent)
        }
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    
    
        val methodChannel = MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "methodchannel"
        )

        methodChannel.setMethodCallHandler {
    
     call, result ->
            if (call.method == "flutterMethod"){
    
    
                Log.d("flutterMethod", "flutter调用native方法")
                result.success("native传递数据到flutter")
            }
        }
    }
  • 在flutter端创建MethodChannel,注意method名称和native那边的一致。
  void getNativeMethod() async{
    
    
    String result = await _platform.invokeMethod("flutterMethod");
    print("flutterMethod:$result");
  }

结果:
在这里插入图片描述

2.2.2 Native调用Flutter的方法

native调用Flutter方法,如果在FlutterActivity里面调用Flutter页面的方法,此时是无法调用到的,因为Flutter页面还没初始化,没加载进来。所以如果要在native的FlutterActivity里面加载Flutter页面里面的方法,则需要保证Flutter页面已经初始化且加载了。可以考虑:

  • FlutterActivity页面延迟调用Flutter方法
  • 通过Flutter调用原生方法,在原生方法里面实现调用Flutter方法。

以下是Flutter调用原生,并在原生里面调用flutter,此时flutter一定已经初始化并加载了。

完整代码请看原生Android调用Flutter的dart方法

2.3 EventChannel

待续…

猜你喜欢

转载自blog.csdn.net/u013293125/article/details/125491116