Flutter通信渠道插件communication_channels的使用

Flutter通信渠道插件communication_channels的使用

本包帮助你在不同的隔离(例如服务、覆盖层等)之间请求执行代码,并且能够注册你将要使用的数据类型,从而轻松地发送和接收数据。这些不同的隔离我们称之为“通道”。

描述

一个通道基本上是一个新的隔离,你可以从任何其他通道向它发送和接收数据。所有的通道都有一个我称之为“通信提供者”的东西,它是通道应该通过的方式进行通信。有一种叫做SocketComProvider的提供者,它通过建立一个服务器并通过它进行通信。如果你使用的是Flutter,则更倾向于使用IsolateComProvider

abstract class _ChannelNameServer {
  const _ChannelNameServer._();
  static SendPort? lookupPortByName(String portName) => 
    IsolateNameServer.lookupPortByName(portName);
  static bool registerPortWithName(SendPort port, String portName) => 
    IsolateNameServer.registerPortWithName(port, portName);
  static bool removePortNameMapping(String portName) => 
    IsolateNameServer.removePortNameMapping(portName);
}

class IsolateSender extends ProviderSender{
  final String portName;
  const IsolateSender(this.portName) : assert(portName != "");

  @override
  Future<bool> send(SentChannelMessageType msg) async {
    final sendPort = _ChannelNameServer.lookupPortByName(portName);
    if(sendPort == null){return false;}
    sendPort.send(msg);
    return true;
  }

  @override
  List<Object?> get props => [portName];
}

class IsolateComProvider extends ComProvider<IsolateSender>{
  String get portName => sender.portName;

  @override
  final IsolateSender sender;

  ReceivePort? _recPort;

  IsolateComProvider({
    required String portName,
  }) : sender = IsolateSender(portName);

  @override
  Future<Stream<SentChannelMessageType>> getReceiverBroadcastStream() async {
    _recPort = ReceivePort();
    _ChannelNameServer.registerPortWithName(_recPort!.sendPort, portName);
    return _recPort!.asBroadcastStream()
        .takeWhile((msg) => msg is SentChannelMessageType,)
        .cast<SentChannelMessageType>();
  }

  @override
  Future<void> closeStreamObj() async {
    _ChannelNameServer.removePortNameMapping(portName);
    _recPort?.close();

    _recPort = null;
  }
}

使用方法

  1. 定义所有可能发送或接收的数据类型

    更多关于这一步的细节可以查看easy_serialization包的示例。

    final customSerializableObjects = <
      SerializationConfig>[
      SerializationConfig<Offset>(
        toMarkupObj: (obj) => {
          "dx": obj.dx,
          "dy": obj.dy,
        },
        fromMarkupObj: (markup) => Offset(
          markup["dx"],
          markup["dy"],
        ),
      ),
      ShapeFillType.values.config,
      SerializationConfig.abstract<Shape>(),
      SerializationConfig.serializable<Circle>(Circle.fromMarkup),
      SerializationConfig.serializable<Rectangle>(Rectangle.fromMarkup),
      SerializationConfig.serializable<Square>(Square.fromMarkup),
    ];
    
  2. 定义你将要使用的通道

    final mainChannel = ChannelType(
      code: 0, debugName: "MAIN",
      provider: SocketComProvider(host: InternetAddress.tryParse("127.0.0.1"), port: 5000,),
    );
    
    final isolateChannel = ChannelType(
      code: 1, debugName: "ISOLATE",
      provider: SocketComProvider(host: InternetAddress.tryParse("127.0.0.1"), port: 5001,),
    );
    
    /// 传递你定义的所有通道。
    final channelsDefinitions = ChannelsDefinitions([mainChannel, isolateChannel]);
    
  3. 创建你将要使用的通道函数

    通道函数基本上是一个你希望能够在任何通道上调用并执行在任何其他通道上的普通函数(带参数和返回类型)。但是它的参数和返回类型必须注册。以下是一个通道函数的例子:

    // 这里我们添加函数配置。
    final allAvailableChannelFunctions = <ChannelFunctionConfig>[
      PrintIsolateHashCode.config,
    ];
    
    /// 这个函数展示了 [callFunWithArgs] 是在 [isolateChannel] 上执行的。
    /// 它还返回了 [isolateChannel] 的 [hashCode]。
    class PrintIsolateHashCode extends ChannelFunction<int> {
      @override
      late final List<Prop> args = [];
    
      PrintIsolateHashCode() : super.to(
        toChannel: isolateChannel,
      );
    
      @override
      TypeAsync<int> callFunWithArgs() async => Isolate.current.hashCode;
    
      //*////////////////////////// DO NOT MODIFY THIS SECTION BY HAND //////////////////////////*//
    
      @override
      ChannelFunctionConfig getConfig() => config;
      static final config = ChannelFunctionConfig<PrintIsolateHashCode>(
        templateFunction: PrintIsolateHashCode._temp,
        fromMarkup: PrintIsolateHashCode.fromJson,
      );
      PrintIsolateHashCode._temp() : super.temp();
      PrintIsolateHashCode.fromJson(Map<String, dynamic> json) : super.fromJson(json);
    }
    

    注意:创建新的通道函数可能会显得复杂,但我在/example/snippets目录下提供了代码片段,你可以在VS CodeAndroid Studio中配置这些代码片段,这将帮助你更快地创建它们。

  4. 最后,我们需要注册我们的通道函数和属性,然后我们可以开始使用我们的通道函数,执行它们并检查和使用它们的结果

    Future<void> initChannelCommunications(ChannelType channel) async {
      /// 配置可发送到通道的注册类型。
      /// 这一点在`easy_serialization`包的示例中详细解释。
      Prop.registerSerializationConfigs(customSerializableObjects);
    
      // 配置正在使用的通道函数。
      ChannelFunction.register(allAvailableChannelFunctions);
    
      // 注册此通道并初始化它。
      await ThisChannel().init(
        channelType: channel,
        definitions: channelsDefinitions,
      );
    }
    
    void main() async {
      print("Main hashcode ${Isolate.current.hashCode}");
    
      // 在第一调用。
      await initChannelCommunications(mainChannel);
    
      await Isolate.spawn(isolateMain, "");
    
      // 等待隔离通道创建完成。
      await isolateChannel.waitUntilStart();
    
      /// 任何 [ChannelFunction] 返回和 [ChannelResult]
      /// 包含错误类型的 [ChannelResult](如果发生错误)。
      final isolateHashCodeRes = await PrintIsolateHashCode().raise();
      print("The isolate hashcode is ${isolateHashCodeRes.correctData}");
    
      await Future.delayed(const Duration(seconds: 2));
    
      // 当我们完成时,这里断开连接。
      await ThisChannel().deAttach();
    }
    
    void isolateMain(String str) async {
      print("isolate hashcode ${Isolate.current.hashCode}");
    
      // 在第一调用。
      await initChannelCommunications(isolateChannel);
    
      await Future.delayed(const Duration(seconds: 2));
    
      // 当我们完成时,这里断开连接。
      await ThisChannel().deAttach();
    }
    

工具

有一些你可以使用的实用函数。

  1. 你可以广播一个通道函数到所有通道。

    await PrintIsolateHashCode().broadcast();
    

更多关于Flutter通信渠道插件communication_channels的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter通信渠道插件communication_channels的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


communication_channels 是一个用于 Flutter 的插件,旨在简化和标准化跨平台(如 Flutter 与原生代码之间)的通信。它提供了一种方便的方式来定义和使用通信渠道,以便在 Flutter 和原生代码之间传递数据。以下是使用 communication_channels 插件的基本步骤和示例。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 communication_channels 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  communication_channels: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 在 Flutter 端定义通信渠道

在 Flutter 端,你可以使用 CommunicationChannel 类来定义通信渠道。你可以为不同的通信需求定义多个渠道。

import 'package:communication_channels/communication_channels.dart';

// 定义一个通信渠道
final myChannel = CommunicationChannel<String, String>(
  name: 'my_channel',
  encoder: (message) => message,
  decoder: (message) => message,
);

在这个例子中,myChannel 是一个用于传递字符串消息的通信渠道。

3. 在原生端注册通信渠道

在原生端(Android 或 iOS),你需要注册与 Flutter 端对应的通信渠道。以下是如何在 Android 和 iOS 上进行注册的示例。

Android

MainActivity.ktMainActivity.java 中注册通信渠道:

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import com.example.communication_channels.CommunicationChannelPlugin

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        CommunicationChannelPlugin.registerChannel(flutterEngine.dartExecutor.binaryMessenger, "my_channel")
    }
}

iOS

AppDelegate.swift 中注册通信渠道:

import UIKit
import Flutter
import communication_channels

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller = window?.rootViewController as! FlutterViewController
    CommunicationChannelPlugin.registerChannel(with: controller.binaryMessenger, channelName: "my_channel")
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

4. 在 Flutter 端发送和接收消息

你可以使用 myChannel 来发送和接收消息。

// 发送消息到原生端
myChannel.send('Hello from Flutter').then((response) {
  print('Received response: $response');
});

// 监听来自原生端的消息
myChannel.receive((message) {
  print('Received message: $message');
  return 'Response from Flutter';
});

5. 在原生端处理消息

在原生端,你需要处理来自 Flutter 的消息并返回响应。

Android

MainActivity.ktMainActivity.java 中处理消息:

CommunicationChannelPlugin.setMessageHandler("my_channel") { message, reply ->
    println("Received message: $message")
    reply.reply("Response from Android")
}

iOS

AppDelegate.swift 中处理消息:

CommunicationChannelPlugin.setMessageHandler("my_channel") { message, reply in
    print("Received message: \(message)")
    reply("Response from iOS")
}

6. 处理错误和清理

确保在应用关闭或不再需要通信渠道时,正确地清理资源。

[@override](/user/override)
void dispose() {
  myChannel.dispose();
  super.dispose();
}
回到顶部