Flutter和Native通信方案

一、整体架构

第一层:路由层(Flutter Boost )

负责页面跳转,统一管理Flutter和Native的页面导航

第二层:Channel通信层 (MethodChannel,EventChannel,BasicMessageChannel)

负责数据和方法调用,Flutter调Native或者Native推数据

第三层:Plugin实现层

负责Channel的管理和生命周期,统一注册和初始化


我们的Flutter和Native通信方案架构主要分为 Flutter Boost 路由层,主要负责Flutter和Native页面的统一管理和跳转、Channel通信层,负责flutter和native通信,主要包含MethodChannel,EventChannel,BasicMessageChannel三种、plugin实现层,是native层对channel的实现,包含了统一注册管理

二、路由层(最上层)

先说路由层,采用的是Flutter Boost 库进行路由,之所以采用Flutter Boost 路由,是因为第一它可以解决

Flutter和Native页面栈管理

官方的方案中flutter页面和native页面有两个页面栈,native页面和flutter页面交替打开,返回时可能不符合预期,比如native1代开flutter1,再打开native1,返回时,直接返回到native1 了,

FlutterBoost 将 Flutter 页面和native页面都统一到原生页面栈中进行管理。这样返回的时候就是符合预期的,大致的原理就是它打开flutter页面是利用了一个flutteractivity作为容器去承载flutter页面,这个flutteractivity是原生的activity,所以会位于原生的页面栈中

第二它有

引擎复用机制

官方的方案中每次打开一个flutter页面都初始化一个flutter engine,

FlutterBoost 通过复用单个 Flutter 引擎实例,避免了官方多引擎方案中频繁创建引擎的性能损耗(引擎初始化比较耗时、另外多个引擎会导致内存占用高)。

但是 FlutterBoost 利用了 Flutter 底层的一个特性:渲染引擎(Engine)和承载画面的画布(View / Surface)是可以解耦、动态绑定的。

大致的的原理就是在一个flutter页面显示需要一个flutterview 和FlutterEngine,FlutterEngine(引擎):负责在后台跑 Dart 代码、处理逻辑、计算布局。它是不长眼睛的,只负责"画图"。FlutterView(画布):是原生的组件(Android 的 SurfaceView 或 iOS 的 UIView )。它负责把引擎画好的东西"显示"在手机屏幕上,并接收用户的触摸事件,当flutter A页面进入打fluttre b页面时,FlutterBoost 发现页面 A 要被盖住了,它立刻把 FlutterEngine FlutterView A 上"剥离(Detach)" 下来,然后把这个 FlutterEngine 立刻"附着(Attach)" 到新页面的 FlutterView B 上。然后进行flutter b页面的代码运行,布局绘制以及显示,为了不让底下的页面 A 变成一片空白,FlutterBoost 在引擎离开页面 A 的一瞬间,会把页面 A 的最后一帧画面"截个图(Bitmap/Snapshot)"贴在 FlutterView A

生命周期同步
  • 双向同步 :原生容器的生命周期事件(如 onCreateonResumeonDestroy)通过消息通道同步到 Flutter 侧,Flutter 页面的 Widget 生命周期(如 initStatedispose)与原生容器保持一致。

统一路由管理:开发者无需关心底层实现,只需通过路由名跳转,简化混合开发复杂度

2.4 Flutter Boost初始化

在Application的onCreate里初始化:

FlutterBoost初始化的时候要传入一个FlutterBoostDelegate,然后实现它的pushNativeRoute和pushFlutterRoute,对应打开本地页面和flutter页面,flutter打开一个页面时,先判断是否是是flutter页面,然后FlutterBoostDelegate的pushNativeRoute或者pushFlutterRoute去打开本地或者flutter页面

native页面打开flutter页面是通过FlutterBoost.instance().open(options),然后也是间接调用到FlutterBoostDelegate的pushFlutterRoute去打开flutter页面

pushNativeRoute就是根据页面名称启动对应的activity

pushFlutterRoute就是启动我们自定义的FlutterBoostActivity,然后根据参数去初始化对应的flutter页面

kotlin 复制代码
object FlutterFacade {
    fun initEngineIfNeed(application: Application) {
        FlutterBoost.instance().setup(application, object : FlutterBoostDelegate {
            // Flutter调Native页面
            override fun pushNativeRoute(options: FlutterBoostRouteOptions) {
                openNativePage(options)
            }
            
            // Flutter调Flutter页面
            override fun pushFlutterRoute(options: FlutterBoostRouteOptions) {
                // 创建FlutterActivity并启动
                val intent = FlutterBoostActivityNew.CachedEngineIntentBuilder(
                    FlutterBoostActivityNew::class.java
                )
                    .backgroundMode(backgroundMode)
                    .url(options.pageName())           // 路由名
                    .urlParams(options.arguments())   // 参数
                    .build(context)
                context.startActivity(intent)
            }
        }, { engine: FlutterEngine ->
            // 引擎初始化完成后注册Plugin
            for (clas in plugins) {
                engine.plugins.add(clas.newInstance())
            }
        }, FlutterBoostSetupOptions.Builder()
            .isDebugLoggingEnabled(Api.DEBUG_MODE)
            .shouldOverrideBackForegroundEvent(true)  // 接管生命周期
            .build()
        )
    }
}

亮点:

  1. 统一栈管理:Native和Flutter页面统一导航,用户体验流畅
  2. Engine复用:多个Flutter页面共享一个Engine,首屏秒开
  3. 生命周期接管:接管前后台事件,保证Flutter页面状态和App状态同步

三、Channel层

3.1 三种Channel对比

类型 通信方向 返回值 适用场景
MethodChannel Flutter→Native 必须Result回调 获取Native数据或执行Native功能
EventChannel Native→Flutter sink推送,无返回值 实时数据推送,长时间的数据流
BasicMessageChannel 双向皆可 可选Reply 耗时复杂操作
Channel 类型 Native → Flutter Flutter → Native
MethodChannel​ ✅ 主动调用 ✅ 主动调用
BasicMessageChannel​ ✅ 发送消息 ✅ 发送消息
EventChannel​ ✅ 推送数据 ❌ 不能推送数据

选择原则:

  • Flutter先发起、需要结果 → MethodChannel
  • Native主动推送 → EventChannel
  • 耗时操作 → BasicMessageChannel

MethodChannel 主要负责Flutter→Native调用,获取获取Native数据或执行Native能力

EventChannel 主要是Native向Flutter推送数据,比如币种价格变化推送

BasicMessageChannel 用于双向发送消息,比较频繁的通信

3.2 MethodChannel:请求-响应模式

整体流程

Flutter调用 → invokeMethod → MethodChannel → MethodCallHandler处理

业务逻辑

Flutter接收 ← Result.success/error ← MethodChannel ← 结果返回

kotlin 复制代码
// 获取国际化字符串
static Future<String?> getString(String localizedLabel) => ChannelManager.commonPlatformChannel
.invokeMethod<String>(BridgeMethod.getString, localizedLabel);
    
class CommonPlugin : BasePlugin() {
    private var channel: MethodChannel? = null
    private val name = CHANNEL_COMMON


override fun connectChannel(messenger: BinaryMessenger, context: Context) {
        Logger.tag(LoganTag.TAG_FLUTTER_BRIDGE).d("connectChannel $name")
        handler = CommonBridgeImpl()
        channel = MethodChannel(messenger, name).apply {
setMethodCallHandler(handler)
        }
}

class CommonBridgeImpl : MethodChannel.MethodCallHandler, IFlutterCommonBridge, IFlutterHandler {

    private val scope get(): CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
    private val dataAnalysisMarketRepo by lazy { DataAnalysisMarketRepo() }

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        Logger.tag("FlutterBridge").i("[${Thread.currentThread()}] onMethodCall ${call.method}")
        try {
            when (call.method) {
                COMMON_GET_STRING -> {
                    result.success(getString(call.arguments as String))
                }
markdown 复制代码
MethodChannel(请求-响应)
├── CommonChannel(通用能力)
│   ├── getHost - 获取host
│   ├── getVersion - 获取版本号
│   ├── getLocalized - 获取当前语言
│   ├── isUserLogin - 判断登录状态
│   ├── getUserInfo - 获取用户信息
│   └── ...共30+方法
├── NetworkChannel(网络请求)
│   └── request - 统一HTTP请求
├── UCenterChannel(用户中心)
│   ├── changeLanguage - 切换语言
│   └── changeValuation - 切换计价方式
└── TradeChannel(交易记录)
    └── ...

EventChannel(订阅-推送)
├── ws_channel:spot - 现货行情流
└── ws_channel:contract - 合约行情流

BasicMessageChannel(双向消息)
├── getDepositQRCodeImage - 生成充值二维码
└── compressImage - 图片压缩

PlatformView(原生组件嵌入)
├── BarChartView - K线图
└── OrderBookView - 订单簿

CommonPlugin负责通用数据或者能力调用,比如获取当前语言,获取用户信息,判断登录状态,复制粘贴

UCenterPlugin负责用户中心设置调用,比如切换语言,修改涨跌色,修改计价方式,等

NetworkPlugin - 网络请求通道

因为native网络请求比较成熟,另外可以进行统一的错误处理,以及添加一些公共逻辑,比如带上cookie,签名超时重试,缓存响应等等,所以构建了一个NetworkPlugin

  1. 统一错误处理:有错误码解析、错误提示逻辑,证书校验等

  2. 公共逻辑多:Token注入、签名、超时重试、响应缓存

Flutter将请求方法、URL、参数、Headers等包装成json参数,传递给native,Native解析后调用封装好的网络工具(复用的是之前js的网络请求接口)

Flutter传过来的JSON包含请求方法、URL、参数、Headers等。Native解析后调用封装好的网络工具,获取网络请求后,将结果包装成json传递回调个flutter

Channel名称:com.kcex.flutter.plugin/network_channel

class NetworkBridgeImpl :

3.3 EventChannel:订阅-推送模式

整体流程

Flutter订阅 → EventChannel → StreamHandler.onListen

Native后台监听数据源 → 数据变化 → sink.success(data) → Flutter接收

Flutter取消订阅 → StreamHandler.onCancel → 清理资源

kotlin 复制代码
Stream<List<TickerEntity>?> marketSpotTickerStream() {
  return _spotMarketEventChannel.receiveBroadcastStream().asyncMap((msg) => _mapToTicker(msg, PairType.spot));
}


class WsPlugin : BasePlugin() {
    private val wsSpotMarketName = CHANNEL_WS_SPOT
private val wsContractMarketName = CHANNEL_WS_CONTRACT

private var wsSpotMarketChannelHandler: WsBridgeImpl? = null
    private var wsContractMarketChannelHandler: WsBridgeImpl? = null

    override fun connectChannel(messenger: BinaryMessenger, context: Context) {
        wsSpotMarketChannelHandler =  WsBridgeImpl(wsSpotMarketName)
        wsContractMarketChannelHandler =  WsBridgeImpl(wsContractMarketName)
        EventChannel(messenger, wsSpotMarketName).setStreamHandler(wsSpotMarketChannelHandler)
        EventChannel(messenger, wsContractMarketName).setStreamHandler(wsContractMarketChannelHandler)
    }


class WsBridgeImpl(val channel: String) : EventChannel.StreamHandler {
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    private var sink: EventChannel.EventSink? = null
    private var tickerJob: Job? = null

    override fun onListen(p0: Any?, p1: EventChannel.EventSink?) {
        sink = p1 ?: return
        when (channel) {
            CHANNEL_WS_SPOT -> tickerJob = handleSpotTickerStream()
            CHANNEL_WS_CONTRACT -> tickerJob = handleContractTickerStream()
        }
    }

WsPlugin - WebSocket行情通道

Channel名称:

  • 现货:com.kcex.flutter.plugin/ws_channel:spot
  • 合约:com.kcex.flutter.plugin/ws_channel:contract

为什么不用轮询?

轮询有三个问题:浪费资源、延迟高、服务器压力大。EventChannel是发布-订阅模式,行情变化才推送,延迟毫秒级。

为什么加100ms debounce?

行情数据一秒好几次,如果每次变化都推送,Flutter UI会频繁重建。加了debounce后,只推送最后一次,兼顾实时性和性能。

key为什么用缩写?

全称:"timeZoneRate"、"currentPrice"、"symbolName"、"priceScale" 缩写:"tzr"、"c"、"nm"、"ps"

省30%流量,对高频推送场景效果明显。

3.4 BasicMessageChannel:双向消息模式

整体流程

Flutter发送 → BasicMessageChannel → MessageHandler处理

耗时操作(子线程)

Flutter接收 ← Reply.reply ← 主线程切换 ← 结果返回

BasicMessagePlugin

kotlin 复制代码
class MessageChannelTool {
  MessageChannelTool._();

  static const basicMessageChannel =
      BasicMessageChannel('com.kcex.flutter.plugin/basic_message', StandardMessageCodec());

  static Future<TransferResult> transformData(TransferParam param) async {
    return basicMessageChannel
.send(param.toJson())
        .then((value) => TransferResult.fromNative(value));
  }
}


class BasicMessagePlugin : BasePlugin() {
    private val name = CHANNEL_BASIC_MASSAGE
private var channel: BasicMessageChannel<Any>? = null
    private var messageHandler: BasicMessageChannel.MessageHandler<Any>? = null


    override fun connectChannel(messenger: BinaryMessenger, context: Context) {
        Logger.tag(LoganTag.TAG_FLUTTER_BRIDGE).d("connectChannel $name")
        messageHandler = BasicMessageHandler()
        channel = BasicMessageChannel(messenger, name, StandardMessageCodec()).apply  {
setMessageHandler(messageHandler)
        }
}
    
    class BasicMessageHandler<T> : BasicMessageChannel.MessageHandler<T> {
    override fun onMessage(
        message: T?,
        reply: BasicMessageChannel.Reply<T>
    ) {
        message ?: return
        Logger.tag("FlutterBridge").i("[${Thread.currentThread()}] onMessage: $message")
        try {
            val mapParams = message as Map<*, *>
            val method = mapParams["method"]
            when (method) {
                SYMBOL_DATA_CONVERSION -> {
                    val data = mapParams["data"] as String
                    val gson = Gson()
                    val convertData = gson.fromJson(data, SymbolDataConvertData::class.java)
                    val resultJson = gson.toJson(
                        NumberFmtUtil.symbolDataConversionImpl(convertData)
                    )
                    reply.reply(createResult(MESSAGE_RESULT_OK, data = resultJson))
                }

Channel名称:com.kcex.flutter.plugin/basic_message

适用场景:生成二维码、图片压缩、数据转换等耗时操作。

为什么不用MethodChannel?

MethodChannel的Result是同步返回的,如果Native处理耗时长,Flutter会一直等待,可能超时。BasicMessageChannel通过Reply异步返回,不阻塞Flutter。

另外适用于频繁交换数据的场景,比如把编辑框的数量,进行国际化展示等


四、Plugin层(承上启下)

4.1 为什么需要Plugin机制

Flutter官方设计了FlutterPlugin接口,让Plugin的生命周期由Engine自动管理,不用手动register/unregister。

定义了一个BasePlugin基类:实现了FlutterPlugin和IConnectChannel接口

实现FlutterPlugin可以将Plugin和Engine绑定,在IConnectChannel中有connectChannel在onAttachedToEngine时候调用,teardownChannel方法在onDetachedFromEngine时候调用,子类实现这两个方法在connectChannel绑定对应的handler实现对应的逻辑,在teardownChannel释放资源,

另外还有一个onActivityResult,在自定义的flutteractivity的onActivityResult中调用,用于从相册选取图片或者扫码时回传数据

kotlin 复制代码
abstract class BasePlugin : FlutterPlugin, IConnectChannel {
    var handler: MethodChannel.MethodCallHandler? = null

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        // Engine关联时初始化
        connectChannel(binding.binaryMessenger, binding.applicationContext)
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        // Engine解绑时销毁,防止内存泄漏
        teardownChannel()
    }
    
    
    为什么需要Activity结果穿透?Flutter调起Native相机、扫码、分享后,结果会回到Activity。如果不穿透,Flutter就收不到回调。
    // 穿透Activity结果回调
    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (handler is IFlutterHandler) {
            (handler as IFlutterHandler).onActivityResult(requestCode, resultCode, data)
        }
    }
}

interface IConnectChannel {
    fun connectChannel(messenger: BinaryMessenger, context: Context)
    fun teardownChannel()
}

每个Plugin实现这个接口,connectChannel里初始化Channel,teardownChannel里释放资源。

4.3 Plugin注册

FlutterFacade统一注册8个Plugin:

csharp 复制代码
object FlutterFacade {
    val plugins: MutableList<Class<out BasePlugin>> = mutableListOf(
        NetworkPlugin::class.java,       // 网络请求
        CommonPlugin::class.java,        // 通用业务
        UCenterPlugin::class.java,       // 用户中心
        TradeRecordPlugin::class.java,   // 交易记录
        BasicMessagePlugin::class.java,  // 耗时消息
        WsPlugin::class.java,          // WebSocket
        FlutterNativeViewPlugin::class.java, // PlatformView
        FlutterServiceBridge::class.java,    // 服务桥接
    )
}
Engine初始化时批量注册:
FlutterBoost.instance().setup(..., { engine: FlutterEngine ->
    for (clas in plugins) {
        engine.plugins.add(clas.newInstance())
    }
}, ...)
亮点:
开闭原则:新增Plugin只需加一行代码
生命周期托管:Engine自动管理,不用手动调用
统一风格:所有Plugin遵循同样模式

注册的时候。维护一个Plugin 的class列表,在FlutterBoost初始化时,Engine初始化时(FlutterBoost.instance().setup),通过clas.newInstance()初始化实例然后添加到engine对应的plugins列表中。

六、PlatformView层(最底层)

6.1 解决的问题

Flutter绘制能力很强,但有些场景还是需要原生组件:

  • 已有成熟库(K线图用MPAndroidChart)
  • 硬件能力(相机、传感器)
  • 系统级控件(分享面板、支付键盘)

6.2 原理:三层渲染

Flutter Widget

PlatformViewLink.onCreatePlatformView

Engine 分配 viewId

initSurfaceAndroidView

Android 端创建真实 View

sql 复制代码
┌──────────────────────────────────┐
│        Activity Window            │
│                                  │
│  ┌────────────────────────────┐ │
│  │   Flutter Surface (GPU)     │ │
│  │                            │ │
│  │   ┌────────────────────┐   │ │
│  │   │   Hole (透明区域)   │   │ │
│  │   └────────────────────┘   │ │
│  │                            │ │
│  └────────────────────────────┘ │
│             ↑                    │
│      Android Native View         │
│      (真实 View,非 Texture)   │
│                                  │
└──────────────────────────────────┘

合成显示(最关键)

sql 复制代码
Flutter Surface(GPU)
    ↑
 透明区域(Hole Punch)
    ↑
原生 View(真实 View)
    ↑
系统 WindowManager / CoreAnimation
    ↓
屏幕

Flutter 负责 布局和遮罩

原生 View 负责 自己绘制

最终由 操作系统合成

AndroidViewSurface是干什么的?

不是 View

是一个 Surface + 挖洞控制器

作用是:

告诉 Flutter:这里要让原生 View 透出来

管理手势命中测试(hitTest)

Flutter 显示原生 View 是通过 PlatformView 实现的。

Flutter 并不会直接绘制原生 View,而是在自己的 Layer 树中预留一个位置,通过 PlatformViewLink 和AndroidViewSurface 创建占区域,再由原生端创建真实的 View 并 attach 到 Activity Window,添加的地方就是flutter预留的透明区域,

最终由系统的 WindowManager 将 Flutter Surface 和原生 View 合成显示。

NativeView不仅首次创建设置数据,还要根据tab切换更新数据

在flutter侧,定义了一个NativeView,在build方法中Android使用PlatformViewLink,iOS使用UiKitView显示本地视图,在onCreatePlatformView属性中,根据传递过来的params.id,将创建的本地视图id记录,用于后续控制,然后调用PlatformViewsService.initSurfaceAndroidView将params.id,viewType,创建参数(creationParams,包括type和data)设置进去,传递给native进行视图创建

didUpdateWidget方法中(当父 Widget 重建,但 State 对象被复用时)使用了MethodChannel调用本地的update方法,根据视图id,以及新的data去重绘视图

在Android侧,自定义一个plugin(FlutterNativeViewPlugin)继承baseplugin,在onAttachedToEngine中将自定义的platformViewFactory注册到platformViewRegistry进行绑定,在自定义的platformViewFactory的oncreate方法中可以根据创建参数的type创建不同类型的视图,然后将对应的id记录到列表中,用于后续控制区分,这里主要是柱状图和折线图,然后根据创建参数中的数据,给视图设置对应的数据,

在connectChannel中会创建对应的MethodChannel,去实现flutter中对应的update方法,根据id去区分要更新哪个视图,然后给视图设置对应新的数据

6.3 Flutter侧使用

kotlin 复制代码
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';

const nativeViewChannelName = 'com.kcex.flutter.plugin/native_view';
const _channel = MethodChannel(nativeViewChannelName);

class _NativeViewPlugin {

  static Future<void> update(int id, dynamic data) {
    return _channel.invokeMethod<void>('update', {
      'id': id,
      'data': data
    });
  }

  static Future<void> dispose(int id) {
    return _channel.invokeMethod<void>('dispose', {
      'id': id,
    });
  }
}

typedef DataConverter = dynamic Function(dynamic data);

class NativeView<T> extends StatefulWidget {

  final T? data;
  final DataConverter? dataConverter;
  final String viewType;

  const NativeView({
    super.key,
    this.data,
    this.dataConverter,
    required this.viewType,
  });

  @override
  State<StatefulWidget> createState() {
    return _State();
  }
}

class _State extends State<NativeView> {

  int? _id;

  @override
  void didUpdateWidget(covariant NativeView oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (_id != null && widget.data != null && widget.dataConverter != null) {
      _NativeViewPlugin.update(_id!, widget.dataConverter!.call(widget.data));
    }
  }

  @override
  Widget build(BuildContext context) {
    final creationParams = {
      'viewType':  widget.viewType,
      if (widget.data != null && widget.dataConverter != null)
        'data':  widget.dataConverter!.call(widget.data)
    };
    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        return PlatformViewLink(
          viewType: nativeViewChannelName,
          surfaceFactory: (context, controller) {
            return AndroidViewSurface(
              controller: controller as AndroidViewController,
              gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
              hitTestBehavior: PlatformViewHitTestBehavior.opaque,
            );
          },
          onCreatePlatformView: (params) {
            _id = params.id;
            return PlatformViewsService.initSurfaceAndroidView(
              id: params.id,
              viewType: nativeViewChannelName,
              layoutDirection: TextDirection.ltr,
              creationParams: creationParams,
              creationParamsCodec: const StandardMessageCodec(),
              onFocus: () {
                params.onFocusChanged(true);
              },
            )
              ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
              ..create();
          },
        );
      case TargetPlatform.iOS:
        return UiKitView(
          viewType: nativeViewChannelName,
          onPlatformViewCreated: (int id) {
            _id = id;
          },
          layoutDirection: TextDirection.ltr,
          creationParams: creationParams,
          creationParamsCodec: const StandardMessageCodec(),
          gestureRecognizers: {
            Factory<OneSequenceGestureRecognizer>(
              () => EagerGestureRecognizer(),
            ),
          },
        );
      default:
        return const Text('unsupported');
    }
  }

  @override
  void dispose() {
    super.dispose();
    if (_id != null) {
      _NativeViewPlugin.dispose(_id!);
    }
  }
}


class FlutterNativeViewPlugin : BasePlugin() {

    private val platformViewFactory = FlutterNativeViewFactory()

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        binding.platformViewRegistry.registerViewFactory(CHANNEL_NATIVE_VIEW, platformViewFactory)
        super.onAttachedToEngine(binding)
    }

    override fun connectChannel(messenger: BinaryMessenger, context: Context) {
        MethodChannel(messenger, CHANNEL_NATIVE_VIEW).setMethodCallHandler { call, result ->
when (call.method) {
                "update" -> {
                    val args = call.arguments as Map<String, Any>
                    val id = args["id"] as Int
                    val view = platformViewFactory.getView(id)
                    if (view != null) {
                        val data = args["data"]
                        if (data == null) {
                            result.success(0)
                            return@setMethodCallHandler
                        }
                        view.update(data)
                    }
                    result.success(0)
                }
                "dispose" -> {
                    val args = call.arguments as Map<String, Any>
                    val id = args["id"] as Int
                    platformViewFactory.removeView(id)
                    result.success(0)
                }
                else -> {
                    result.success(0)
                }
            }
        }
}

    override fun teardownChannel() {
        platformViewFactory.clearViews()
    }
}

class FlutterNativeViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {

    private val _views = mutableMapOf<Int, ViewUpdater>()

    override fun create(context: Context, id: Int, args: Any?): PlatformView {
        val argsMap = args as Map<String, Any>
        val viewType = argsMap["viewType"].toString()
        val data = argsMap["data"]
        val view = when (viewType) {
            "BarChartView" -> FlutterBarChartView(context)
            "LineChartView" -> FlutterLineChartView(context)
            else -> throw UnsupportedOperationException("$args not support")
        }
        if (data != null) {
            view.update(data)
        }
        _views[id] = view
        return view
    }

    fun getView(id: Int) = _views[id]

    fun clearViews() {
        _views.clear()
    }

    fun removeView(id: Int) {
        val view = _views.remove(id) as PlatformView
        view.dispose()
    }
}

6.5 亮点

  1. 性能60fps:比Flutter自绘的30fps高一倍,直接利用GPU渲染
  2. 原生能力复用:直接用MPAndroidChart,不用重写
  3. 双向通信:Flutter可以控制Native View,Native View可以回调Flutter
  4. 数据隔离:每个PlatformView有独立viewId,数据不会串

五、数据层

5.1 WebSocket数据流

┌─────────────┐ ┌─────────────┐ ┌─────────────┐

│ 服务端 │ ───→ │ Native端 │ ───→ │ Flutter端 │

│ 推送数据 │ │ EventChannel│ │ 更新UI │

└─────────────┘ └─────────────┘ └─────────────┘

Native端维护WebSocket连接,收到数据后通过EventChannel推送给Flutter。

5.2 HTTP请求数据流

┌─────────────┐ ┌─────────────┐ ┌─────────────┐

│ Flutter端 │ ───→ │ Native端 │ ───→ │ 服务端 │

│ MethodCall │ │ HTTP请求 │ │ 返回数据 │

└─────────────┘ └─────────────┘ └─────────────┘

Flutter通过MethodChannel传请求参数,Native发起HTTP请求,结果通过Result回调返回。


七、总结

层次 核心组件 亮点
路由层 Flutter Boost 统一页面栈、Engine复用、弹窗支持
Channel层 5 Method + 2 Event + 1 BasicMessage 按业务域分层、职责单一
Plugin层 BasePlugin 生命周期托管、Activity穿透
数据层 WebSocket + HTTP debounce防抖、key缩写省流量
视图层 PlatformView 60fps性能、原生组件复用

核心亮点:

  1. 分层解耦:路由、Channel、Plugin各层分离,耦合度低
  2. 生命周期托管:Engine自动管理Plugin,不用手动register
  3. 统一页面栈:Flutter Boost解决混合导航问题
  4. 性能优化:debounce防抖、key缩写、PlatformView 60fps
  5. 开闭原则:新增Plugin只需加一行代码
相关推荐
2601_957888564 小时前
分布式新媒体架构:短视频矩阵系统的技术痛点、算法规则与效率优化实践
分布式·架构·媒体
Safeploy安策数据5 小时前
从算法到架构:构建企业级数据库加密与密钥防护体系的实战手册
数据库·安全·架构
爱勇宝6 小时前
前端工程师的下一站:不是失业,而是 AI Engineer
前端·javascript·架构
wb043072016 小时前
外卖大战——从阿明的“3 秒生死线“,看系统性能优化的全链路方法论
开发语言·性能优化·架构·php
2601_957882246 小时前
跨异构网络媒体中台架构:多渠道接口幂等性、高性能流式管线与数据网关设计
架构·aigc
江华森7 小时前
Nacos 微服务注册与配置中心深度学习指南
微服务·云原生·架构
货拉拉技术7 小时前
货拉拉标注平台-拉拉标注
后端·架构
龙亘川7 小时前
拆解低空智联:四位一体架构、落地场景与行业瓶颈|《低空智联技术与应用白皮书 2026》深度复盘
架构·低空智联技术与应用白皮书
用户337922545687 小时前
从零手搓大语言模型:模型结构篇
架构