一文教你更简单的使用event_bus,不再写繁杂的代码,手动调用取消订阅的方法。
event_bus
是 Dart 中一个非常实用的事件总线库,它基于发布 - 订阅模式,允许应用程序的不同部分之间进行松耦合的通信。
基本使用
1. 创建事件类
首先,你需要定义不同类型的事件,每个事件通常是一个简单的类。例如:
// 定义事件类
class UserLoggedInEvent {
final String username;
UserLoggedInEvent(this.username);
}
2. 创建 EventBus
实例
在应用程序中创建一个 EventBus
实例,通常将其作为单例使用,以便在整个应用中共享。
import 'package:event_bus/event_bus.dart';
// 创建 EventBus 单例
EventBus eventBus = EventBus();
3. 订阅事件
使用 eventBus.on<T>()
方法来订阅特定类型的事件。这个方法返回一个 Stream
,你可以监听这个流来处理事件。
// 订阅 UserLoggedInEvent 事件
var userLoggedInSubscription = eventBus.on<UserLoggedInEvent>().listen((event) {
print('User ${event.username} logged in.');
});
// 订阅 UserLoggedOutEvent 事件
var userLoggedOutSubscription = eventBus.on<UserLoggedOutEvent>().listen((event) {
print('User logged out.');
});
4. 发布事件
使用 eventBus.fire()
方法来发布事件。
// 发布 UserLoggedInEvent 事件
eventBus.fire(UserLoggedInEvent('JohnDoe'));
// 发布 UserLoggedOutEvent 事件
eventBus.fire(UserLoggedOutEvent());
5. 取消订阅
当你不再需要监听某个事件时,应该取消订阅以避免内存泄漏。
// 取消订阅
userLoggedInSubscription.cancel();
userLoggedOutSubscription.cancel();
思考
封装event_bus的目的可能有几个方面:简化API,添加日志或错误处理,支持单例模式,或者整合其他功能如依赖注入。我们希望有一个更友好的接口,比如通过注解自动订阅事件,或者统一管理事件监听器的生命周期,防止内存泄漏。
event_bus包本身提供了基本的发布和订阅功能,但需要我们手动管理订阅的取消。封装的时候,可以考虑自动取消订阅,比如结合Flutter的Widget生命周期,或者在Dispose时自动取消。或者,我们可能希望支持多种事件类型,或者对事件进行过滤和处理。可能还需要添加日志功能,方便调试事件触发和监听的情况。
考虑错误处理。原生的event_bus可能在事件处理函数抛出异常时导致整个应用崩溃,封装时可以添加try-catch块,捕获异常并记录,避免应用崩溃。同时,允许我们自定义错误处理逻辑。
另外,可能还需要支持不同的执行方式,比如同步和异步事件处理。原生的event_bus可能默认是同步的,但有时候异步处理更合适,封装时可以提供一个选项来选择执行方式。
关于订阅管理,我不希望手动调用取消订阅的方法,而是希望通过某种方式自动管理。例如,在Flutter中,可以在StatefulWidget的dispose方法中自动取消订阅。这时候,封装类可能需要提供一个简便的方法,比如在注册事件监听时返回一个订阅对象,并在dispose时调用取消。
接下来,需要添加一些高级功能,比如事件过滤,只接收特定条件下的事件,或者转换事件数据。或者支持多个不同的EventBus实例,以便在不同模块中使用独立的事件总线。
现在,结合这些思考,我设计一个封装类的结构
- 单例模式:确保全局只有一个EventBus实例,或者允许创建多个实例。
- 订阅方法:提供一个on方法,用于注册事件监听器,返回订阅对象以便管理。
- 发布方法:提供一个sendEvent方法,用于触发事件。
- 自动取消订阅:比如在Flutter中,结合Widget生命周期自动取消。
- 日志记录:在发布和接收事件时输出日志,方便调试。
- 错误处理:捕获监听器中的异常,避免应用崩溃。
- 支持泛型:确保能够处理不同类型的事件。
以下是针对 event_bus
的封装实现,提供 单例管理、自动取消订阅、日志跟踪、错误处理 等功能,并支持与 Flutter Widget 生命周期无缝集成:
/// 封装后的高级事件总线
class AppEventBus {
static final EventBus _instance = EventBus();
// 私有构造,确保单例
AppEventBus._internal();
/// 获取单例实例
static EventBus get instance => _instance;
/// 发送事件
static void sendEvent<T>(T event) {
if (kDebugMode) {
print('[EventBus] Firing event: ${event.runtimeType}');
}
instance.fire(event);
}
/// 订阅事件,返回可取消的订阅对象
static StreamSubscription<T> on<T>(void Function(T event) handler, {
bool handleError = true,
ErrorCallback? onError,
}) {
final subscription = instance.on<T>().listen((event) {
if (kDebugMode) {
print('[EventBus] Received event: ${event.runtimeType}');
}
_safeRun(() => handler(event), onError: onError);
}, onError: handleError ? (error, stack) {
_safeRun(() => onError?.call(error, stack));
} : null);
return subscription;
}
static void _safeRun(void Function() action, {ErrorCallback? onError}) {
try {
action();
} catch (e, s) {
if (kDebugMode) {
print('[EventBus] Handler error: $e\n$s');
}
onError?.call(e, s);
}
}
}
/// Flutter Widget 集成扩展
mixin EventBusMixin<T extends StatefulWidget> on State<T> {
final List<StreamSubscription> _eventSubscriptions = [];
/// 安全订阅事件,自动管理生命周期
void subscribe<Event>(void Function(Event event) handler, {
bool handleError = true,
ErrorCallback? onError,
}) {
_eventSubscriptions.add(
AppEventBus.on<Event>(handler, handleError: handleError, onError: onError)
);
}
@override
void dispose() {
for (final sub in _eventSubscriptions) {
sub.cancel();
}
if (kDebugMode) {
print('[EventBus] Canceled ${_eventSubscriptions.length} subscriptions');
}
super.dispose();
}
}
typedef ErrorCallback = void Function(Object error, StackTrace stackTrace);
使用示例
定义两个事件
import 'package:flutter/material.dart';
// 定义事件类
class UserLoggedInEvent {
final String username;
UserLoggedInEvent(this.username);
}
// 定义另一个事件类
class DataUpdatedEvent {
final String data;
DataUpdatedEvent(this.data);
}
写一个page测试
class EventBusPage extends StatefulWidget {
const EventBusPage({super.key});
@override
State<EventBusPage> createState() => _EventBusPageState();
}
class _EventBusPageState extends State<EventBusPage> with EventBusMixin {
@override
void initState() {
super.initState();
print('initState');
// 自动管理订阅
subscribe<UserLoggedInEvent>(handleLogin);
subscribe<DataUpdatedEvent>(dataUpdated);
}
void handleLogin(UserLoggedInEvent event) {
print('User logged in: ${event.username}');
}
void dataUpdated(DataUpdatedEvent event) {
print("更新数据:${event.data}");
}
@override
void dispose() {
print('dispose---EventBusPage');
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: 'EventBus',
actions: [
],
onBackPressed: () {
// Handle back button press, if needed
Navigator.pop(context);
},
),
body: Container(
alignment: Alignment.center,
child: Column(
children: [
SizedBox(height: 50),
Container(
height: 50,
width: 200,
color: Colors.red,
child:
TextButton(onPressed: () {
// 发送事件
AppEventBus.sendEvent(UserLoggedInEvent('Alice'));
}, child: Text("用户昵称按钮"))),
SizedBox(height: 50),
Container(
height: 50,
width: 200,
color: Colors.red,
child:
TextButton(onPressed: () {
// 发送事件
AppEventBus.sendEvent(DataUpdatedEvent("kkkkk"));
}, child: Text("普通更新按钮"))),
],
),
),
);
}
}
功能亮点
- 1、单例模式 全局唯一事件总线实例,通过
AppEventBus.instance
访问核心功能 - 2、类型安全 强类型事件处理,编译时类型检查
- 3、生命周期管理
- 通过
EventBusMixin
自动取消订阅 - 手动订阅返回
StreamSubscription
便于管理
- 通过
- 4、安全防护
- 异常捕获机制防止事件处理崩溃
- 错误处理回调支持
- 5、调试支持
- 开发模式下的详细事件日志
- 清晰的错误堆栈打印
- 6、灵活扩展
- 支持同步/异步事件处理
- 可配置的错误处理策略
原理分析
class EventBus {
StreamController _streamController;
StreamController get streamController => _streamController;
EventBus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
EventBus.customController(StreamController controller)
: _streamController = controller;
Stream<T> on<T>() {
if (T == dynamic) {
return streamController.stream as Stream<T>;
} else {
return streamController.stream.where((event) => event is T).cast<T>();
}
}
void fire(event) {
streamController.add(event);
}
void destroy() {
_streamController.close();
}
}
EventBus的源码非常少,原理也非常的简单
1、类定义和成员变量
class EventBus {
StreamController _streamController;
/// Controller for the event bus stream.
StreamController get streamController => _streamController;
}
_streamController
:私有成员变量,用于管理事件的发布和订阅。streamController
:一个 getter 方法,用于获取_streamController
。
2、构造函数
EventBus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
EventBus.customController(StreamController controller)
: _streamController = controller;
-
默认构造函数
EventBus({bool sync = false})
:sync
参数:如果为true
,事件会在调用fire
方法时直接传递给监听器;如果为false
(默认值),事件会在创建事件的代码执行完成后再传递给监听器。- 使用
StreamController.broadcast(sync: sync)
创建一个广播流控制器,允许有多个订阅者监听同一个流。
-
自定义控制器构造函数
EventBus.customController(StreamController controller)
:- 允许用户传入自定义的
StreamController
,例如使用 RxDart 的Subject
作为控制器。
- 允许用户传入自定义的
3、订阅事件方法 on<T>()
Stream<T> on<T>() {
if (T == dynamic) {
return streamController.stream as Stream<T>;
} else {
return streamController.stream.where((event) => event is T).cast<T>();
}
}
- 该方法用于订阅特定类型
T
的事件。 - 如果
T
为dynamic
,则返回流控制器的原始流,包含所有类型的事件。 - 如果
T
为其他类型,使用where
方法过滤出类型为T
的事件,然后使用cast
方法将事件转换为T
类型。 - 返回的
Stream
是广播流,允许多个订阅者同时监听。
4、发布事件方法 fire(event)
void fire(event) {
streamController.add(event);
}
该方法用于发布一个事件,将事件添加到流控制器中,流控制器会将事件广播给所有订阅者。
5、销毁事件总线方法 destroy()
void destroy() {
_streamController.close();
}
该方法用于销毁事件总线,关闭流控制器,释放资源
实现原理细节
StreamController
:EventBus
类内部使用StreamController
来管理事件的发布和订阅。StreamController.broadcast()
创建一个广播流控制器,允许有多个订阅者监听同一个流。Stream
:on
方法返回一个Stream
对象,订阅者可以通过listen
方法监听这个流。Stream
是 Dart 中用于处理异步事件序列的抽象,它提供了一系列的操作符,如where
和cast
,用于过滤和转换事件。- 事件过滤:在
on
方法中,如果指定了事件类型T
,则会使用where
方法过滤出类型为T
的事件,然后使用cast
方法将事件转换为T
类型。
总结
event_bus
库通过 StreamController
和 Stream
实现了事件总线模式,允许不同部分的代码之间进行松耦合的通信。发布者通过 fire
方法发布事件,订阅者通过 on
方法订阅事件,事件总线负责将事件广播给所有订阅者。