diff --git a/README.md b/README.md index 26b5b92..6725bbd 100644 --- a/README.md +++ b/README.md @@ -51,44 +51,6 @@ bridge.share( ); ``` -### 监听来自 Flutter 的消息 (支持一对多监听) - -现在 `FlutterBridge.instance.on` 支持注册多个监听器,并且 `on` 方法会返回一个 `VoidCallback` 函数,调用该函数可以精确取消本次注册的监听。此外,新增了 `once` 方法用于一次性监听。 - -```dart -// 注册监听器 (一对多) -// on 方法返回一个用于取消当前监听的函数 -final cancelTranslationListener = FlutterBridge.instance.on( - FromFlutterAppEnum.translateResult.code, - (data) { - print("收到翻译结果: ${data['data']}"); - // 处理翻译结果 - }, -); -// 当不再需要某个监听时,调用其返回的函数来精确取消 -cancelTranslationListener.call();; -// 注册另一个监听器 -final anotherListener = FlutterBridge.instance.on( - FromFlutterAppEnum.translateResult.code, - (data) { - print("另一个监听器收到翻译结果: ${data['data']}"); - }, -); -// 当不再需要某个监听时,调用其返回的函数来精确取消 -cancelTranslationListener.call();; - -// 仅监听一次 (一次性监听,不用手动取消,调用一次后自动取消) -FlutterBridge.instance.once( - FromFlutterAppEnum.shareFinished.code, - (data) { - print("分享完成 (一次性监听): $data"); - }, -); - -// 如果需要移除某个类型的所有监听器,可以使用 off 方法 -// 警告:这将移除 FromFlutterAppEnum.translateResult.code 对应的所有监听器 -FlutterBridge.instance.off(FromFlutterAppEnum.translateResult.code); -``` ## API 文档 @@ -102,8 +64,8 @@ FlutterBridge.instance.off(FromFlutterAppEnum.translateResult.code); final bridge = FlutterBridge.instance; ``` -#### 消息监听方法 - +### 监听来自 Flutter 的消息 (支持一对多监听) +### 不走队列。这是“广播”模式 ##### `on(String type, Function(Map) callback)` 注册一个消息监听器。现在支持为同一 `type` 注册多个监听器。 @@ -126,27 +88,13 @@ final cancelListener2 = bridge.on(FromFlutterAppEnum.translateResult.code, (data // 当不再需要第一个监听器时,调用其返回的函数 cancelListener1.call();// 仅移除监听器1 +// 当不再需要第二个监听器时,调用其返回的函数 +cancelListener2.call();// 仅移除监听器2 ``` ##### `off(String type)` -##### `once(String type, Function(Map) callback)` - -注册一个【一次性】消息监听器。该监听器在收到消息后会自动移除。 - -- `type`: 消息类型(使用 `FromFlutterAppEnum` 中的 code) -- `callback`: 回调函数,接收消息数据 - -```dart -bridge.once(FromFlutterAppEnum.shareFinished.code, (data) { - print("收到分享完成事件: $data"); - // 处理一次性逻辑 -}); -``` - -##### `off(String type)` - -取消注册某个 `type` 的【所有】消息监听器。此方法主要用于兼容旧代码或需要一次性清空所有同类型监听的场景。 +取消注册某个 `type` 的【所有】消息监听器。此方法主要用于需要一次性清空所有同类型监听的场景。 - `type`: 要取消监听的【所有】消息类型 @@ -232,14 +180,22 @@ bridge.share( ); ``` -##### 翻译功能 - +##### 翻译功能(发送翻译消息+监听返回结果) +##### 只有当你调用 sendRequest 方法时,才会走队列 ```dart -bridge.translateRequest({ - 'text': 'Hello', - 'from': 'en', - 'to': 'zh', -}); +bridge.sendRequest( + sendType: ToFlutterAppEnum.translateRequest, // 1. 发送类型 + listenType: FromFlutterAppEnum.translateResult, // 2. 监听类型 + params: { + 'text00001': 'text00001', + 'text00002': 'text00002', + 'text00003': 'text00003', + }, // 3. 参数 + onSuccess: (data) { + // 4. 成功回调 (自动排队,安全) + print("翻译结果: $data"); + }, +); ``` ##### 直播间相关 @@ -328,24 +284,29 @@ Flutter 向 H5 发送的消息类型枚举。包含: ```dart import 'package:web_tools/web_tools.dart'; -void setupTranslation() { - final bridge = FlutterBridge.instance; - - // 注册翻译结果监听器(once不用手动清理,内部自动清理监听) - bridge.once(FromFlutterAppEnum.translateResult.code, (data) { - final translatedText = data['data']['text']; - print('翻译结果: $translatedText'); - // 更新 UI 显示翻译结果 - }); - - // 发送翻译请求 - bridge.translateRequest({ - 'text': 'Hello World', - 'from': 'en', - 'to': 'zh', - }); -} +//需要翻译的map +var textMap = {}; +void setupTranslation() { + // 使用 sendRequest (发送请求并队列监听回调,内部自动释放监听回调),该方法只监听一次, + // 如果需要多次监听,请使用 FlutterBridge.instance.on,并且手动释放监听回调 + FlutterBridge.instance.sendRequest( + sendType: ToFlutterAppEnum.translateRequest, // 1. 发送类型 + listenType: FromFlutterAppEnum.translateResult, // 2. 监听类型 + params: textMap, // 3. 参数 + onSuccess: (data) { + // 4. 成功回调 (自动排队,安全) + print("翻译结果: $data"); + + final Map result = + data.map((key, value) => MapEntry(key, value.toString())); + + textMap.clear();// 清空临时变量 + FlutterBridge.instance.textMap.addAll(translated);//全局变量追加翻译结果 + update(['your_id']); + }, + ); + } ``` ### 完整示例:分享功能 @@ -353,29 +314,51 @@ void setupTranslation() { ```dart import 'package:web_tools/web_tools.dart'; -void setupShare() { - final bridge = FlutterBridge.instance; +class SantaLogic extends GetxController { - // 注册分享完成监听器 - bridge.on(FromFlutterAppEnum.shareFinished.code, (data) { - print('分享完成'); - // 处理分享完成后的逻辑 - }); - - // 触发分享 - bridge.share( - activityId: 'activity_123', - needShareReport: true, - extraParams: {'source': 'web'}, - ); + // 1. 定义一个变量来持有取消函数 + VoidCallback? _cancelShareListener; + + @override + void onReady() { + super.onReady(); + + // 2. 注册监听(需要监听分享回调时) + // on 方法现在会返回一个取消函数,专门用来取消“这一个”监听 + _cancelShareListener = FlutterBridge.instance.on( + FromFlutterAppEnum.shareFinished.code, + (data) { + print("SantaLogic: 收到分享成功的广播"); + // 执行业务,比如刷新任务列表 + requestTaskInfo(); + } + ); + // 1. 触发分享(有些分享不需要监听,只需要触发分享) + FlutterBridge.instance.share( + activityId: 'activity_123', + needShareReport: true, + extraParams: {'source': 'web'}, + ); + } + + @override + void onClose() { + // 3. ⚠️⚠️⚠️ 如果注册了监听,必须在页面销毁时调用取消函数 + // 如果不调用,SantaLogic 即使退出了,这个闭包还在 Bridge 里, + // 下次分享成功时,代码还会跑,且 SantaLogic 无法被垃圾回收! + _cancelShareListener?.call(); + + super.onClose(); + } } + ``` ## 注意事项 1. **单例模式**:`FlutterBridge` 使用单例模式,在整个应用中只有一个实例。 -2. **监听器管理**:使用 `on()` 注册的监听器在使用完毕后应该使用`_listener1.call()` 取消注册,避免内存泄漏, `off()` 这将移除对应type的所有监听器。`once()`方法不用手动取消监听,内部自动清理监听。 +2. **监听器管理**:使用 `on()` 注册的监听器在使用完毕后必须使用`_listener1.call()` 取消注册,避免内存泄漏, `off()` 慎用,这将移除对应type的所有监听器。`sendRequest()`方法是融合了发送消息+监听回调,不用手动取消监听,内部自动清理监听。并且只监听一次,防止多次回调。 3. **消息格式**:所有消息都遵循 `{'type': '消息类型', 'data': {...}}` 的格式。 diff --git a/lib/utils/app_bridge.dart b/lib/utils/app_bridge.dart index 7cbd00e..96c1e38 100644 --- a/lib/utils/app_bridge.dart +++ b/lib/utils/app_bridge.dart @@ -131,6 +131,15 @@ enum FromFlutterAppEnum { @JS() external void sendMessageToNative(String data); +/// 🆕 [新增] 通用请求任务模型 +class _RequestTask { + final String sendType; // 发送给 Native 的类型 + final Map params; // 发送的参数 + final Function(Map) callback; // 成功回调 + + _RequestTask(this.sendType, this.params, this.callback); +} + class FlutterBridge { static final FlutterBridge instance = FlutterBridge._internal(); @@ -139,6 +148,12 @@ class FlutterBridge { final _messageListeners = )>>{}; var textMap = {}; //多语言翻译 + /// 🆕 [新增] 通用请求队列池 (Key: listenType, Value: 任务列表) + final Map> _requestQueues = {}; + + /// 🆕 [新增] 忙碌状态池 (记录哪些 listenType 当前正在等待 Native 回复) + final Set _activeResponseTypes = {}; + FlutterBridge._internal() { _initListener(); } @@ -193,21 +208,6 @@ class FlutterBridge { }); } - /// 仅监听一次,触发后自动移除 - void once(String type, Function(Map) callback) { - // 定义一个引用,用于在回调内部取消自己 - VoidCallback? cancelRef; - - // 注册监听 - cancelRef = on(type, (data) { - // 1. 立即移除监听 - cancelRef?.call(); - - // 2. 执行真正的业务回调 - callback(data); - }); - } - /// 3. 核心修改:on 方法 /// 返回一个 VoidCallback,调用它即可取消本次监听 VoidCallback on(String type, Function(Map) callback) { @@ -236,14 +236,86 @@ class FlutterBridge { }; } - /// 4. 兼容旧代码的 off 方法 - /// 警告:这会移除该类型下的【所有】监听器 + /// 警告:这会移除该类型下的【所有】监听器 (慎用,通常用于全局重置,一旦调用,该 type 别的地方监听器将失效) void off(String type) { if (_messageListeners.containsKey(type)) { _messageListeners.remove(type); print("All listeners for '$type' have been removed."); } } +// ========================================================= + // 👇 🆕 [新增] 核心通用请求方法 (替代了旧的 requestTranslate) + // ========================================================= + + /// 发送请求并等待回调 (自动排队,防并发) + /// [sendType] 发送给 Native 的类型 + /// [listenType] 等待 Native 回复的类型 + /// [params] 参数 + /// [onSuccess] 成功回调 + void sendRequest({ + required ToFlutterAppEnum sendType, + required FromFlutterAppEnum listenType, + required Map params, + required Function(Map) onSuccess, + }) { + final responseKey = listenType.code; + + // 1. 初始化该类型的队列 + if (!_requestQueues.containsKey(responseKey)) { + _requestQueues[responseKey] = []; + } + + // 2. 入队 + _requestQueues[responseKey]!.add(_RequestTask( + sendType.code, + params, + onSuccess, + )); + + // 3. 调度执行 + _processRequestQueue(responseKey); + } + + /// 🆕 [新增] 内部队列调度方法 + void _processRequestQueue(String responseKey) { + // 如果该类型正在忙,或者队列空了,直接返回 + if (_activeResponseTypes.contains(responseKey) || + (_requestQueues[responseKey]?.isEmpty ?? true)) { + return; + } + + // 1. 标记忙碌 + _activeResponseTypes.add(responseKey); + + // 2. 取出队首任务 + final task = _requestQueues[responseKey]!.removeAt(0); + + // 3. 注册临时监听 (使用 on 的多播特性) + VoidCallback? cancelRef; + + cancelRef = on(responseKey, (data) { + try { + final innerData = data['data'] ?? {}; + // 执行业务回调 + task.callback(innerData); + } catch (e) { + print("Queue processing error: $e"); + } finally { + // --- 任务闭环 --- + // A. 移除当前临时监听 (不影响全局监听) + cancelRef?.call(); + + // B. 解除忙碌状态 + _activeResponseTypes.remove(responseKey); + + // C. 递归处理下一个任务 + _processRequestQueue(responseKey); + } + }); + + // 4. 发送 Native 消息 + sendToFlutter(task.sendType, task.params); + } // 发送消息给 App(通过 WebView 调用 JS 方法) void sendToFlutter(String type, Map data) { @@ -277,8 +349,8 @@ class FlutterBridge { sendToFlutter(ToFlutterAppEnum.createGame.code, {'gameId': gameId}); void wantToPlay(String gameId) => sendToFlutter(ToFlutterAppEnum.wantToPlay.code, {'gameId': gameId}); - void translateRequest(Map data) => - sendToFlutter(ToFlutterAppEnum.translateRequest.code, data); + // void translateRequest(Map data) => + // sendToFlutter(ToFlutterAppEnum.translateRequest.code, data);//移除,建议使用sendRequest方法 void toRecharge() => sendToFlutter(ToFlutterAppEnum.toRecharge.code, {}); void toRedDiamond() => sendToFlutter(ToFlutterAppEnum.toRedDiamond.code, {}); void jumpToH5(String path, String title) => sendToFlutter(