关于flutter:Flutter插件开发指南02-事件订阅-EventChannel

43次阅读

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

Flutter 插件开发指南 02: 事件订阅 EventChannel

视频
https://www.bilibili.com/video/BV1zj411d7k4/
前言
上一节咱们讲了 Channel 通道,然而如果你是卫星定位业务,原生端被动推音讯给 Flutter 这时候就要用到 EventChannel 通道了。
本节会写一个 1~50 的计数器,到 50 后主动敞开原生的音讯订阅。

FlutterEventChannel

FlutterEventChannel 的作用是在 Flutter 平台和原生平台之间建设双向通信的桥梁。通过 FlutterEventChannel,Flutter 应用程序能够向原生平台发送事件,同时也能够接管来自原生平台的事件。
FlutterEventChannel 能够用于许多场景,例如:

传感器数据采集:许多应用程序须要从设施的传感器(如加速度计、陀螺仪、磁力计等)中获取数据。Flutter 应用程序能够通过 FlutterEventChannel 发送申请,让原生平台采集传感器数据并返回到 Flutter 应用程序中。后台任务实现告诉:当应用程序在后盾运行时,可能须要执行一些长时间运行的工作。Flutter 应用程序能够通过 FlutterEventChannel 向原生平台发送申请,让原生平台在后台任务实现时发送告诉到 Flutter 应用程序中。音频和视频流传输:许多应用程序须要在 Flutter 应用程序和原生平台之间传输音频和视频流。Flutter 应用程序能够通过 FlutterEventChannel 向原生平台发送申请,让原生平台传输音频和视频流并返回到 Flutter 应用程序中。这使得 Flutter 应用程序能够应用原生平台的音频和视频解决性能,以进步应用程序的性能和用户体验。
FlutterEventChannel 执行过程如下:

Flutter 应用程序创立一个 FlutterEventChannel 对象,并指定一个惟一的通道名称。Flutter 应用程序调用 FlutterEventChannel 的 receiveBroadcastStream 办法,以获取一个 Stream 对象,以便监听来自原生平台的事件。原生平台创立一个 EventChannel 对象,并指定与 Flutter 应用程序中通道名称相匹配的字符串。原生平台调用 EventChannel 的 setStreamHandler 办法,以设置一个 StreamHandler 对象,以便接管来自 Flutter 应用程序的事件并向其发送原生事件。当 Flutter 应用程序须要向原生平台发送事件时,它会将事件数据发送到 FlutterEventChannel 对象中。FlutterEventChannel 将事件数据传递给原生平台的 EventChannel 对象。EventChannel 对象将事件数据传递给 StreamHandler 对象中的 onListen 办法。在 onListen 办法中,原生平台能够执行一些操作并发送事件数据到 Stream 对象中。Flutter 应用程序能够通过 Stream 对象监听来自原生平台的事件,并执行相应的操作。
须要留神的是,FlutterEventChannel 中应用的 Stream 对象是异步的,因而在监听来自原生平台的事件时须要应用异步编程的技术。另外,在应用 FlutterEventChannel 时,Flutter 应用程序和原生平台之间须要约定好通道名称和事件数据格式,以便可能正确地交互和解决数据。

原文 https://ducafecat.com/blog/flutter-plugin-event-channel

参考
https://api.flutter.dev/flutter/services/EventChannel-class.html
https://mobikul.com/event-channel-in-flutter/
步骤
Flutter 插件
接口定义
lib/flutter_plugin_add_platform_interface.dart
  Future<bool?> startCounting() {    throw UnimplementedError(‘startCounting() has not been implemented.’);  }
原生调用
lib/flutter_plugin_add_method_channel.dart
  @override  Future<bool?> startCounting() async {    final val = await methodChannel.invokeMethod<bool>(‘startCounting’);    return val;  }
插件调用类
lib/flutter_plugin_add.dart
// 类型定义 – 接管函数 typedef TypeOnRecvData = void Function(int value);
  // event channel 定义  static const eventChannel =      EventChannel(‘com.ducafecat.counter/eventChannel’);  // 订阅  StreamSubscription? _streamSubscription;  // 接管函数  TypeOnRecvData? _onRecvData;
  // 开始计数  Future<void> startCounting(TypeOnRecvData onRecvData) async {_onRecvData = onRecvData;    if (_streamSubscription == null) {bool? isStarting =          await FlutterPluginAddPlatform.instance.startCounting();      if (isStarting == true) {_streamSubscription =            eventChannel.receiveBroadcastStream().listen(_listenStream);      }    }  }
  // 勾销计数  void cancelCounting() {    _streamSubscription?.cancel();    _streamSubscription = null;    _onRecvData = null;  }
  // 接管函数  void _listenStream(value) {debugPrint(“Received From Native:  $value\n”);    _onRecvData?.call(value);    if (value == 50) {cancelCounting();    }  }
  // 开释  void dispose() {    cancelCounting();  }
Flutter 例子
example/lib/main.dart
  // 计数器返回  int counterResult = 0;
  @override  void deactivate() {    // 开释    _flutterPluginAddPlugin.dispose();    super.deactivate();}
  @override  Widget build(BuildContext context) {…                  // 计数 event              Text(‘count: $counterResult’),              ElevatedButton(onPressed: () {_flutterPluginAddPlugin.startCounting((value) {setState(() {counterResult = value;});                  });                },                child: const Text(‘ 开始计数 ’),              ),              ElevatedButton(onPressed: () {_flutterPluginAddPlugin.cancelCounting();                },                child: const Text(‘ 完结计数 ’),              ),
Android 端
android/src/main/java/com/ducafecat/flutter_plugin_add/FlutterPluginAddPlugin.java
成员变量
  // 日志标签  final String TAG_NAME = “From_Native”;  // 事件通道名称  public static final String eventChannelName = “com.ducafecat.counter/eventChannel”;  // 事件通道  private EventChannel.EventSink eventChannel;  // 计数器  private int count = 0;  // 事件 Handler  private Handler eventHandler;  // 消息传递器  private BinaryMessenger binaryMessenger;
保留 BinaryMessenger
  @Override  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {binaryMessenger = flutterPluginBinding.getBinaryMessenger();
启动 onMethodCall
  @Override  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {…          // start    else if (call.method.equals(“startCounting”)) {new EventChannel(binaryMessenger, eventChannelName).setStreamHandler(new EventChannel.StreamHandler() {@Override                public void onListen(Object args, final EventChannel.EventSink events) {Log.w(TAG_NAME, “Adding listener”);                  eventChannel = events;                  count = 0;                  eventHandler = new Handler();                  runnable.run();                }                @Override                public void onCancel(Object args) {Log.w(TAG_NAME, “Cancelling listener”);                  eventHandler.removeCallbacks(runnable);                  eventHandler = null;                  count = 0;                  eventChannel = null;                  System.out.println(“StreamHandler – onCanceled: “);                }              }      );      result.success(true);    }
定时器
  private final Runnable runnable = new Runnable() {    @Override    public void run() {int TOTAL_COUNT = 50;      if (count >= TOTAL_COUNT) {eventChannel.endOfStream();      } else {count++;        Log.w(TAG_NAME, “\nParsing From Native:  ” + count);        eventChannel.success(count);      }      eventHandler.postDelayed(this, 200);    }  };
开释
  @Override  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {channel.setMethodCallHandler(null);    if(eventHandler != null) {eventChannel.endOfStream();      eventHandler.removeCallbacks(runnable);      eventHandler = null;      eventChannel = null;    }  }
IOS 端
定义成员变量
ios/Classes/FlutterPluginAddPlugin.h
// FlutterEventSink@property (nonatomic, strong) FlutterEventSink eventSink;// 定时器 @property (nonatomic, strong) NSTimer *timer;// 计数器 @property (nonatomic, assign) NSInteger counter;
注册 eventChannel
ios/Classes/FlutterPluginAddPlugin.m
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>)registrar {…      // 注册事件通道    FlutterEventChannel eventChannel = [FlutterEventChannel eventChannelWithName:@”com.ducafecat.counter/eventChannel”  binaryMessenger: [registrar messenger]];    [eventChannel setStreamHandler:instance];}
办法调用
– (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {…  else if ([@”startCounting” isEqualToString:call.method]) {result(@(YES));  }
开始订阅
– (FlutterError*)onListenWithArguments:(id)arguments                             eventSink:(FlutterEventSink)eventSink {self.eventSink = eventSink;    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5                                                  target:self                                                selector:@selector(sendEvent)                                                userInfo:nil                                                 repeats:YES];    return nil;}
// 发送音讯 - (void)sendEvent {if (self.eventSink) {self.counter++;      self.eventSink(@(self.counter));  }}
勾销订阅
– (FlutterError*)onCancelWithArguments:(id)arguments {[self.timer invalidate];    self.timer = nil;    self.eventSink = nil;    self.counter = 0;    return nil;}
最初启动

代码
https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_plugin_add
小结
应用 EventChannel 能够让插件开发更加灵便、高效、牢靠和易于应用,从而能够进步插件的品质和用户体验。
还须要特地留神以下几点:

线程安全性:EventChannel 的事件通信是在异步线程上执行的,因而须要确保插件代码和原生代码的线程安全性。如果在插件代码中拜访 UI 线程或其余不平安的线程,可能会导致应用程序解体或其余问题。内存治理:在应用 EventChannel 时须要留神内存治理。如果不及时开释资源,可能会导致内存透露或其余性能问题。倡议在 EventChannel 不再须要时,及时进行事件监听并开释资源。插件生命周期治理:在编写插件时,须要思考插件的生命周期治理,如何在插件被加载和卸载时正确地初始化和开释资源。在 Flutter 中,能够应用 FlutterPlugin 接口中的 onAttachedToEngine 和 onDetachedFromEngine 办法来治理插件的生命周期。数据传输格局:EventChannel 传输的数据格式须要在插件和原生代码之间协商一致。倡议应用规范的数据传输格局,如 JSON 或 Protocol Buffers 等。错误处理:在应用 EventChannel 时,须要思考错误处理。如果事件通信呈现谬误,须要及时处理并向 Flutter 代码报告错误信息,以便及时调试和修复问题。
感激浏览本文
如果我有什么错?请在评论中让我晓得。我很乐意改良。

© 猫哥
ducafecat.com
end

正文完
 0