likes
comments
collection
share

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

作者站长头像
站长
· 阅读数 28

1. 引言

🤡 杰哥常说 Flutter 的本质是一套「UI框架」,解决的是「一套代码在多端的渲染」,而涉及到 跨平台,必然绕不过 跨端通信, Flutter 官方给开发者提供了 Platform Channel,用于实现 Dart 与 不同平台 间的相互通信。

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

Channel 的类型主要有三种,分别适用于不同的通信场景:

  • MethodChannel:主要方式,调用原生方法并接收返回值,适合一次性调用。
  • EventChannel事件流/数据流的持续通信,如监听传感器数据。
  • BasicMessageChannel:传递 字符串或二进制信息,适合 双向通信快速连续传递简单数据

🤔 实际开发中App业务很少是 纯UI 的,基本都会涉及到 Flutter与原生平台的通信,举个例子🌰 → 图片上传需要选择手机里的图片。所以了解 Flutter Channel 相关的 API 用法 & 了解背后的实现原理 还是很重要的,本节我们就来深入学习下相关的姿势。

2. API 使用详解

2.1. MethodChannel

2.1.1. 使用示例

写个 Flutter调原生弹Toast 的例子,先是 Flutter端

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  // 💡 创建 MethodChannel 实例 (配置通道名称)
  static const platform = MethodChannel('cn.coderpig.cp_flutter_anim_demo');

  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('MethodChannel Demo'),
        ),
        body: Align(
          alignment: Alignment.center,
          child: ElevatedButton(
            onPressed: _showToast,
            child: const Text('弹Toast'),
          ),
        ),
      ),
    );
  }

  // 💡 MethodChannel使用的是异步通信
  Future<void> _showToast() async {
    // 💡 调用原生方法
    final String result = await platform.invokeMethod('showToast', 'Flutter调原生弹Toast');
    print(result);
  }
}

Android端

import android.widget.Toast
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    companion object {
        // 💡 通道名称
        const val CHANNEL = "cn.coderpig.cp_flutter_anim_demo"
    }

    // 💡 FlutterEngine配置
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        // 💡 注册 MethodChannel
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            // 💡 判断调用的方法名
            if (call.method == "showToast") {
                // 💡 获取参数
                val message = call.arguments as? String
                if (message != null) {
                    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
                    // 💡 返回结果
                    result.success("Toast showed")
                }
            }
        }
    }
}

运行结果如下

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

梳理下使用流程

  • Flutter 端:创建 MethodChannel 实例 (配置通道名称),通过 异步 调用 invokeMethod(方法名,参数) 来调用平台方法,并对平台端的返回结果进行处理。
  • Android 端:继承 FlutterActivity,重写 configureFlutterEngine() 在其中注册 MethodChannel,对调用的方法名进行判断,读取 参数 执行相关操作,然后通过 result.success() 返回结果给Flutter端。

😄 看完基本用法,接着讨论下封装,毕竟 MethodChannel 是用得最多的一种Channel~

2.1.2. Flutter端-封装示例

Flutter端 一般很少直接把MethodChannel相关的代码写在 UI层,而是抽取到一个单独的 服务类 中,方便 复用维护。然后是常规需要捕获的 两个异常

  • PlatformException平台插件调用失败异常,包含以下属性:code (错误代码,用于标识错误类型)、message (错误信息, String)、details (错误详细信息,dynamic)、stacktrace (平台的堆栈跟踪信息)。
  • MissingPluginException未找到相应平台插件异常,包含属性:message (错误信息)。

😏 其实,还有一个 TypeError(类型转换异常) 也建议做下处理,比如这样的代码:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

如果原生端返回的数据不是 String,是会报错的:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

😁 当然,也可以对 其它异常 也做下捕获 兜底,简单封装代码示例如下:

import 'package:flutter/services.dart';

class PlatformService {
  static const MethodChannel _platform = MethodChannel('cn.coderpig.cp_flutter_anim_demo');

  // 调用原生方法通用处理方法
  static Future<T?> invokeMethod<T>(String method, [dynamic arguments]) async {
    try {
      final T? result = await _platform.invokeMethod(method, arguments);
      return result;
    } on PlatformException catch (e) {
      print("PlatformException 通用处理: ${e.code} - ${e.message} - ${e.details} - ${e.stacktrace}");
      return null;
    } on MissingPluginException catch (e) {
      print("MissingPluginException 通用处理: ${e.message}");
      return null;
    } on TypeError catch (e) {
      print("TypeError 通用处理: ${e}");
      return null;
    } catch (e) {
      print("未知异常 通用处理: $e");
      return null;
    }
  }

  // 调用原生弹Toast
  static Future showToast(String message) => invokeMethod('showToast', message);

  // 调用原生弹Dialog
  static Future showDialog(String message) => invokeMethod('showDialog', message);

  // 获取原生设备信息
  static Future<String?> getDeviceInfo() => invokeMethod('getDeviceInfo');
}

调用处

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

Android 端:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

运行结果如下

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

😆 根据错误提示,把 getDeviceInfo() 的泛型改成 Map<Object?, Object?>? 就能获取到正确的结果啦:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

2.1.3. Android端-封装示例

😄 Android 端也可以做下封装,先是把 MethodChannel 相关逻辑从 FlutterActivity 抽取出来:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

随着项目开发,method 会越来越多,一长串的判断,可能会导致代码的 可读性降低,可以把具体的逻辑抽取成单个 方法

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

😊 当然,也可以抽得更彻底,直接定义 接口,然后每个 Method 各自实现接口:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

🤡 如果想再折腾的话,可以搞下 枚举密封类(scaled) ,把 when 判断逻辑转移到内部,通过 遍历/反射 的方式来进行 动态条件匹配,增删 Method 就不用改判断逻辑了。

💁‍♂️ MethodChannel 这种玩法有点像 网络调用,Flutter 是 客户端,平台端是 服务端,可以借鉴后端那一套,统一制定 通信协议,如:消息传输都走Json,然后无论调用成功与否,都返回 Rest API 风格的响应格式,类似这样:

// 成功
{
  "code": 200,
  "msg": "Success",
  "data": {
    "id": 1,
    "version": "12",
    "email": " Mi MIX 2S"
  }
}

// 异常
{
  "code": 400,
  "msg": "错误请求参数",
  "data": {}
}

这样做的好处是,Flutter 端 可以对响应结果进行 统一解析处理

2.1.4. Android 调 Flutter 端

💁‍♂️ 有时可能有 Android端主动调Flutter端 的需求,也顺带写下代码示例,先是 Android 端:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

然后是 Flutter 端:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

运行后,点击按钮,Android 端收到 Flutter 端返回的数据:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

2.2. EventChannel

2.2.1. 使用示例

写个 Flutter监听Android陀螺仪数据 的例子,先是 Android 端:

// 💡继承 EventChannel.StreamHandler 接口,实现 onListen 和 onCancel 方法
class GyroscopeEventChannelHandler(private val context: Context) : EventChannel.StreamHandler, SensorEventListener {
    private var sensorManager: SensorManager? = null
    private var gyroscope: Sensor? = null
    private var eventSink: EventChannel.EventSink? = null // 事件通道

    // 💡 Flutter 端开始监听EventChannel 时调用
    override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
        eventSink = events
        // 获得传感器管理器
        sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
        // 获得陀螺仪传感器
        gyroscope = sensorManager?.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
        // 注册监听器
        sensorManager?.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_NORMAL)
    }

    // 💡 Flutter 端停止监听EventChannel 时调用
    override fun onCancel(arguments: Any?) {
        // 取消监听器
        sensorManager?.unregisterListener(this)
        // 释放资源
        eventSink = null
    }

    // 传感器数据变化时调用
    override fun onSensorChanged(event: SensorEvent?) {
        event?.let {
            val gyroscopeData = mapOf(
                "x" to it.values[0],
                "y" to it.values[1],
                "z" to it.values[2]
            )
            eventSink?.success(gyroscopeData)
        }
    }

    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}


class MainActivity : FlutterActivity() {
    companion object {
        // 💡 通道名称
        const val GYROSCOPE_CHANNEL = "cn.coderpig.cp_flutter_anim_demo/gyroscope"
    }

    // 💡 FlutterEngine 配置
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        EventChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            GYROSCOPE_CHANNEL
        ).setStreamHandler(GyroscopeEventChannelHandler(this))
    }
}

Flutter 端:

// 💡 初始化监听Channel + 监听原生端传过来的数据
class GyroscopeStreamHandler {
  // 💡 初始化监听陀螺仪的MethodChannel
  static const EventChannel _eventChannel = EventChannel('cn.coderpig.cp_flutter_anim_demo/gyroscope');

  static Stream<Map<String, dynamic>> get gyroscopeStream {
    // 💡 监听陀螺仪数据
    return _eventChannel.receiveBroadcastStream().map((event) => Map<String, dynamic>.from(event));
  }
}

import 'package:flutter/material.dart';
import 'gyroscope_stream_handler.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: GyroscopePage(),
    );
  }
}

class GyroscopePage extends StatefulWidget {
  const GyroscopePage({super.key});

  @override
  State createState() => _GyroscopePageState();
}

class _GyroscopePageState extends State<GyroscopePage> {
  Map<String, dynamic>? _gyroscopeData;

  @override
  void initState() {
    super.initState();
    // 💡 监听陀螺仪数据
    GyroscopeStreamHandler.gyroscopeStream.listen((data) {
      setState(() {
        _gyroscopeData = data;
      });
    }, 
    onDone:(){
      // 流关闭时回调
    },
    onError:(error) {},
      // 流中发生错误时回调
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('陀螺仪数据'),
      ),
      body: Center(
        // 💡 显示陀螺仪数据
        child: _gyroscopeData == null
            ? const Text('无数据')
            : Text('X: ${_gyroscopeData!['x']}\nY: ${_gyroscopeData!['y']}\nZ: ${_gyroscopeData!['z']}'),
      ),
    );
  }
}

运行结果如下

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

梳理下使用步骤

  • Flutter 端:创建 EventChannel 实例,通过 receiveBroadcastStream 接收原生平台的数据流,并转换为Dart 中的 Stream
  • Android 端:实现 EventChannel.StreamHandler 接口,来处理数据流的 创建(onListen)销毁(onCancel)
  • FlutterActivity子类插件注册类注册EventChannel,然后通过 EventSink 实例发送数据,三个可选方法:成功-success(Object),错误-error(errorCode, errorMessage, errorDetails) 和 流结束-endOfStream() Flutter端会触发 onDone() 回调,不再接收任何新事件。

2.3. BasicMessageChannel

2.3.1. 使用示例

写个 Flutter端点击按钮向原生端发消息,原生端计数并返回 的例子,先是 Flutter端 代码:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late BasicMessageChannel<String> _channel;

  String? _response;

  @override
  void initState() {
    super.initState();
    // 💡 初始化 Channel
    _channel = const BasicMessageChannel<String>('basic_channel', StringCodec());
  }

  void _sendMessage() async {
    // 💡 给原生端发送消息,并获取返回值刷新
    final String? reply = await _channel.send('increment');
    setState(() {
      _response = reply;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BasicMessageChannel 示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('原生端响应: ${_response ?? '暂无数据'}'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _sendMessage,
              child: const Text('发送消息'),
            ),
          ],
        ),
      ),
    );
  }
}

Android 端:

class MainActivity : FlutterActivity() {
    private lateinit var messageChannel: BasicMessageChannel<String>    // 懒加载BasicMessageChannel
    private var counter = 0 // 计数器

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        // 💡 创建 BasicMessageChannel
        messageChannel =
            BasicMessageChannel(flutterEngine.dartExecutor.binaryMessenger, "basic_channel", StringCodec.INSTANCE)

        // 💡 设置消息处理器
        messageChannel.setMessageHandler { message, reply ->
            // 处理来自 Flutter 的消息
            if (message == "increment") {
                counter++
                Toast.makeText(this, "收到Flutter端的信息,当前计数器值: $counter", Toast.LENGTH_SHORT).show()
                reply.reply("计数器值: $counter")
            } else {
                reply.reply("未知消息")
            }
        }
    }
}

运行结果如下

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

梳理下使用步骤

  • Flutter 端:创建 BasicMessageChannel 实例 (指定 通道名称 & 编码器),通过该实例的 send() 向原生端发送消息,并处理返回结果。
  • Android 端:创建 BasicMessageChannel 实例 (指定 通道名称 & 编码器),调用 setMessageHandler 设置 消息处理器,处理来自 Flutter 端的消息,并调用 reply.reply() 返回结果给 Flutter 端。

3. MethodChannel 源码探秘

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

对每层的职责有个基本的认识,阅读起源码来更利索,OK,直接开扒🏃‍♀️~

3.1. Framework 层

3.1.1. MethodChannel

点开 MethodChannel 的源码,看下 Structure (类结构):

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

各个 invokeXxxMethod 最终调用的都是 _invokeMethod()

Future<T?> _invokeMethod<T>(String method, { required bool missingOk, dynamic arguments }) async {
  // 💡 根据传入的【method】和【arguments】创建一个【MethodCall】实例
  // 💡 使用【codec.encodeMethodCall】将其编码为【ByteData】格式。
  final ByteData input = codec.encodeMethodCall(MethodCall(method, arguments));

  // 💡【kProfilePlatformChannels】变量用于控制是否启用平台通道的性能分析和统计
  // 💡 判断此变量的值,true → _ProfiledBinaryMessenger.sendWithPostfix()
  // 💡 false -> binaryMessenger.send()
  final ByteData? result =
    kProfilePlatformChannels ?
      await (binaryMessenger as _ProfiledBinaryMessenger).sendWithPostfix(name, '#$method', input) :
      await binaryMessenger.send(name, input);

  // 💡 如果平台端返回结果为null,判断 missingOk 参数的值
  // 💡 true 返回 null,💡 否则抛出 MissingPluginException 异常
  if (result == null) {
    if (missingOk) {
      return null;
    }
    throw MissingPluginException('No implementation found for method $method on channel $name');
  }

  // 💡 平台端返回结果不为null,调用【codec.decodeEnvelope】解码结果,并转换为期望类型T?
  return codec.decodeEnvelope(result) as T?;
}

🤔 逻辑还是比较清晰的,依次看看涉及到的类~

3.1.2. MethodCall

从类结构不难看出它的作用 → 封装方法调用的信息 (方法名+参数)

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

3.1.3. MethodCodec

🙂 就 方法调用 & 封装结果的编解码

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

抽象类,搜下 codec = 看下它的 具体实现类

3.1.4. StandardMethodCodec

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

调用 StandardMessageCodec#writeValue()buffer 里写数据,点开源码:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

🤔 em... 就是将 Dart对象序列化写入到WriteBuffer 中,这是 编码,也看下 解码decodeEnvelope()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟下 StandardMessageCodec#readValue()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

判断缓冲区是否有剩余数据,没有抛FormatException异常,表示消息已损坏。从缓存区读取一个 无符号的8位整数 作为 类型标识,然后调 readValueOfType() 根据这个 类型标识 从缓冲区中读取 相应类型的值 并返回。

3.1.5. BinaryMessenger-信使

😄 了解完 方法调用和封装结果的编解码,接着就到具体怎么 发送消息 了,调的 binaryMessenger.send() ,跟下这个get方法:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

不考虑性能分析,那就是调的 _findBinaryMessenger() 获取实例:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

🤔 判断是否为 Web环境ServicesBinding.rootIsolateTokennull (当前处于main isolate),是-返回 BackgroundIsolateBinaryMessenger.instance 否-返回ServicesBinding.instance.defaultBinaryMessenger。我们通信的平台是 移动端,所以只关注后者。

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

依次跟下代码:ServicesBinding#initInstances()createBinaryMessenger()DefaultBinaryMessenger.()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

前面调的是 send() 方法,跟一跟,最后定位到了 __sendPlatformMessage()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

使用 @Native注解,说它是 native方法,即调用到 Flutter引擎层symbol 是 native代码对应的 函数名,引擎层将 底层C++代码 包装成Dart代码,通过 dart:ui 暴露给 Flutter框架层 调用。🤿 好,Framework层 就跟到这,接下来我们往下游到 Engine 层~

3.2. Engine 层

Github仓库-flutter/engine,🤷‍♀️ 源码几十个G,没有编译和调试引擎的需求,懒得clone了,直接在网页傻瓜硬看,先搜下 symbol 参数对应的函数名,发现👣,路径:lib/ui/dart_ui.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

通过阅读源码,不难知道这个文件的主要作用:

初始化 dart:ui 库,设置 FFI (外部函数接口) 本地函数解析器。

3.2.1. SendPlatformMessage()

又搜了下 SendPlatformMessage,在 lib/ui/window/platform_configuration.cc 发现了它的具体实现:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

💡 :上面的 GetTaskRunners().GetUITaskRunner() 用于获取执行 UI任务Task Runner,为的是保证回调函数在 UI线程 上执行,以避免线程安全问题,确保用户界面的更新操作在正确的线程上进行。

3.2.2. HandlePlatformMessage()

它是一个 函数,看下定义部分的代码:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

调的 UIDartState 对象的 HandlePlatformMessage(),跟一下 lib/ui/ui_dart_state.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

双击方法名检索引用处,第一个名字有 embedder 应该跟 平台层 有关,还没到,直接看第二个:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

点开代码:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

这个 delegate_Engine 类的一个成员变量,用于引用 Engine::Delegate 的接口实现,它在构造函数中完成初始化,搜下 OnEngineHandlePlatformMessage() ,很明显就是这个 shell/common/shell.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

3.2.3. OnEngineHandlePlatformMessage()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

💡 获取 platform_message_handler_弱引用 是为了避免 异步任务中持有强引用,导致潜在的内存泄露或悬挂指针问题。看下它是在哪完成赋值的:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

platform_view 是在 Shell::Setup 函数中赋值的,在 Flutter Engine 启动时,Shell 实例会被创建,Setup() 会被调用以初始化所有必要的组件:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

PlatformViewAndroid 端的实现是 PlatformViewAndroid,路径:shell/platform/android/platform_view_android.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

搜下 platform_message_handler_ ,在 构造函数 中进行了设置:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

搜下 PlatformMessageHandlerAndroid,路径:shell/platform/android/platform_message_handler_android.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

🤔 em... 再搜下 FlutterViewHandlePlatformMessage,路径:shell/platform/android/platform_view_android_jni_impl.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

搜下 g_handle_platform_message_method

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

🤷‍♀️ 行吧,最终调用的 Java 层的 FlutterJNI.java 里的 handlePlatformMessage() ,🤿 跟完 Engine层,接着再来看下 Embedder层-Android的相关实现。

3.3. Embedder 层 (Android)

3.3.1. MethodChannel

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

参数和上面 DartMethodChannel 基本一致,多了个 taskQueue,关注点在 BinaryMessenger(信使) 上,Demo 中通过重写 FlutterActivityconfigFlutterEngine() ,调用 flutterEngine.getDartExecutor().getBinaryMessenger() 拿到 BinaryMessenger实例,然后创建了 MethodChannel

3.3.2. FlutterActivityAndFragmentDelegate

🤔 而 configureFlutterEngine() 是在 FlutterActivityAndFragmentDelegate 的内部接口 Host 中定义的,FlutterActivity 实现了这个接口,并定义了一个 FlutterActivityAndFragmentDelegate 类型的成员变量 delegate,然后在 onCreate() 中进行了初始化:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

而后调用的 onAttach() ,会触发 FlutterEngine 的初始化方法 setUpFlutterEngine()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

em... 就是为 FlutterActivityAndFragmentDelegate 设置一个 FlutterEngine 实例,然后是 dartExecutor,它在 FlutterEngine 的构造方法中进行了初始化:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

点开其中的 onAttachedToJNI()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

这里调用 setPlatformMessageHandler()dartMessenger 设置为 平台消息处理器,这使得 DartExecutor开始处理与 Dart执行上下文双向通信。而 binaryMessenger 也是在 DartExecutor 的构造方法中进行的初始化:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

再跟下上面调用的 setMethodCallHandler

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

本质上调用的 DefaultBinaryMessenger#setMessageHandler()

3.3.3. DefaultBinaryMessenger

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

这里的 messengerDartMessenger,也是在 DartExecutor 的构造方法中初始化的,跟下 setMessageHandler()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

看下 messageHandlers

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

呕吼,就是定义了一个map,来存每个 channel 对应的 handler,然后这个handler的类型是 IncomingMethodCallHandler

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

3.3.4. DartMessenger

上面的 Engine 层,最后跟到了调 FlutterJNI.java 中的 handlePlatformMessage()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

根据前面的代码,我们可以知道这个 platformMessageHandler 其实就是 DartMessenger,跟下它的 handleMessageFromDart()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟下 dispatchMessageToQueue()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟下 invokeHandler()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

这个 handlerIncomingMethodCallHandler,调的也是里面的 onMessage() 方法。

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

到此从 Dart 端发送消息到 Android 端的流程就走完了,接着就是 回传结果Dart 端了。

3.4. 回传结果

都调的 BinaryReply#reply() ,跟下:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟下 FlutterJNI#invokePlatformMessageResponseCallback()

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

点开发现是 native 方法:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

直接在 flutter/engine 仓库搜 InvokePlatformMessageResponseCallback,路径:shell/platform/android/platform_view_android_jni_impl.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

这里获取 GetPlatformMessageHandler() 获取的消息处理器自然是 PlatformMessageHandlerAndroid,路径:shell/platform/android/platform_message_handler_android.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

3.4.1. Complete

由注释得知 message_response 的类型为 PlatformMessageResponseDart,跟下 Complete() ,路径:lib/ui/window/platform_message_response_dart.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

跟下 PostCompletion,看下怎么处理的 转换后的Dart数据

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

这个 tonic 是用于 DartC++ 间交互的库,几个常见API:

  • DartInvoke:用于在 C++ 代码中调用 Dart 函数。
  • DartByteData:用于在 Dart 和 C++ 之间传递字节数据。
  • DartState:管理 Dart 虚拟机的状态。
  • DartPersistentValue:用于在 C++ 代码中持有 Dart 对象的引用。

3.4.2. DartInvoke

路径:third_party/tonic/logging/dart_invoke.cc

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

溯源后,可以发现,这个闭包其实就是在 _DefaultBinaryMessenger#send() 中设置的 回调

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

👏 到此,我们总算把 从Dart端发送消息 → Android端Android端处理后回传结果 → Dart端 的代码调用流程走通了。😄 接着画下图,帮助大家缕清整个调用流程~

3.5. 调用流程图解

3.5.1. Embedder层-Android端

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

3.5.2. Framework层-Flutter端

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

3.5.3. Engine层

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

4. 小结

🤡 花了亿点时间,终于把 MethodChannel 的底层实现原理搞清楚了,😏 真的搞清楚了吗?问个问题:

Android端是在 主线程 中接收方法调用 & 数据回传,如果我开个 耗时异步任务,过好一阵子才调 success() 会怎样?会出现ANR?返回值丢失啥的吗?

比如这样的代码:

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

:🤷‍♀️ 都不会,回传消息调的 BinaryReply#reply()JNI 调的 Engine层(C++) 的方法,通过 tonicDart 里设置的 回调函数(闭包) 。一分钟后,Flutter端依旧会收到回传的结果。

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

运行结果

跟🤡杰哥一起学Flutter (二十五、🔍Flutter Channel 原理探秘)🤡 Flutter 三种 Ch

😄 同理,也不用担心快速触发相同的MethodCall会不会出现结果不匹配的问题 (毕竟传服不同闭包),唯一要担心的可能是:Dart 中使用 await 关键字会 等待异步任务执行完毕 后才继续往下执行,这种写法,没有返回,代码就不会一直往下走。如果对异步结果不关心,就不要用await,当然,也可以使用 then()whenComplete() 来避免堵塞后续代码。

😁 至于另外两个Channel → EventChannel 和 BasicMessageChannel 目前用得不多,后续有必要再另外扒一扒原理吧,本节就说到这,谢谢🙏~

参考文献

转载自:https://juejin.cn/post/7403163989632352292
评论
请登录