与APP交互回调添加队列方法sendRequest

This commit is contained in:
zhulixiao
2026-01-27 17:25:20 +08:00
parent cbd3d8eff8
commit 587af2c22a
2 changed files with 170 additions and 115 deletions

161
README.md
View File

@@ -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 文档 ## API 文档
@@ -102,8 +64,8 @@ FlutterBridge.instance.off(FromFlutterAppEnum.translateResult.code);
final bridge = FlutterBridge.instance; final bridge = FlutterBridge.instance;
``` ```
#### 消息监听方法 ### 监听来自 Flutter 的消息 (支持一对多监听)
### 不走队列。这是“广播”模式
##### `on(String type, Function(Map<String, dynamic>) callback)` ##### `on(String type, Function(Map<String, dynamic>) callback)`
注册一个消息监听器。现在支持为同一 `type` 注册多个监听器。 注册一个消息监听器。现在支持为同一 `type` 注册多个监听器。
@@ -126,27 +88,13 @@ final cancelListener2 = bridge.on(FromFlutterAppEnum.translateResult.code, (data
// 当不再需要第一个监听器时,调用其返回的函数 // 当不再需要第一个监听器时,调用其返回的函数
cancelListener1.call();// 仅移除监听器1 cancelListener1.call();// 仅移除监听器1
// 当不再需要第二个监听器时,调用其返回的函数
cancelListener2.call();// 仅移除监听器2
``` ```
##### `off(String type)` ##### `off(String type)`
##### `once(String type, Function(Map<String, dynamic>) callback)` 取消注册某个 `type` 的【所有】消息监听器。此方法主要用于需要一次性清空所有同类型监听的场景。
注册一个【一次性】消息监听器。该监听器在收到消息后会自动移除。
- `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 ```dart
bridge.translateRequest({ bridge.sendRequest(
'text': 'Hello', sendType: ToFlutterAppEnum.translateRequest, // 1. 发送类型
'from': 'en', listenType: FromFlutterAppEnum.translateResult, // 2. 监听类型
'to': 'zh', params: {
}); 'text00001': 'text00001',
'text00002': 'text00002',
'text00003': 'text00003',
}, // 3. 参数
onSuccess: (data) {
// 4. 成功回调 (自动排队,安全)
print("翻译结果: $data");
},
);
``` ```
##### 直播间相关 ##### 直播间相关
@@ -328,24 +284,29 @@ Flutter 向 H5 发送的消息类型枚举。包含:
```dart ```dart
import 'package:web_tools/web_tools.dart'; import 'package:web_tools/web_tools.dart';
//需要翻译的map
var textMap = <String, String>{};
void setupTranslation() { void setupTranslation() {
final bridge = FlutterBridge.instance; // 使用 sendRequest (发送请求并队列监听回调,内部自动释放监听回调),该方法只监听一次,
// 如果需要多次监听,请使用 FlutterBridge.instance.on并且手动释放监听回调
FlutterBridge.instance.sendRequest(
sendType: ToFlutterAppEnum.translateRequest, // 1. 发送类型
listenType: FromFlutterAppEnum.translateResult, // 2. 监听类型
params: textMap, // 3. 参数
onSuccess: (data) {
// 4. 成功回调 (自动排队,安全)
print("翻译结果: $data");
// 注册翻译结果监听器once不用手动清理内部自动清理监听 final Map<String, String> result =
bridge.once(FromFlutterAppEnum.translateResult.code, (data) { data.map((key, value) => MapEntry(key, value.toString()));
final translatedText = data['data']['text'];
print('翻译结果: $translatedText');
// 更新 UI 显示翻译结果
});
// 发送翻译请求
bridge.translateRequest({
'text': 'Hello World',
'from': 'en',
'to': 'zh',
});
}
textMap.clear();// 清空临时变量
FlutterBridge.instance.textMap.addAll(translated);//全局变量追加翻译结果
update(['your_id']);
},
);
}
``` ```
### 完整示例:分享功能 ### 完整示例:分享功能
@@ -353,29 +314,51 @@ void setupTranslation() {
```dart ```dart
import 'package:web_tools/web_tools.dart'; import 'package:web_tools/web_tools.dart';
void setupShare() { class SantaLogic extends GetxController {
final bridge = FlutterBridge.instance;
// 注册分享完成监听器 // 1. 定义一个变量来持有取消函数
bridge.on(FromFlutterAppEnum.shareFinished.code, (data) { VoidCallback? _cancelShareListener;
print('分享完成');
// 处理分享完成后的逻辑
});
// 触发分享 @override
bridge.share( void onReady() {
super.onReady();
// 2. 注册监听(需要监听分享回调时)
// on 方法现在会返回一个取消函数,专门用来取消“这一个”监听
_cancelShareListener = FlutterBridge.instance.on(
FromFlutterAppEnum.shareFinished.code,
(data) {
print("SantaLogic: 收到分享成功的广播");
// 执行业务,比如刷新任务列表
requestTaskInfo();
}
);
// 1. 触发分享(有些分享不需要监听,只需要触发分享)
FlutterBridge.instance.share(
activityId: 'activity_123', activityId: 'activity_123',
needShareReport: true, needShareReport: true,
extraParams: {'source': 'web'}, extraParams: {'source': 'web'},
); );
}
@override
void onClose() {
// 3. ⚠️⚠️⚠️ 如果注册了监听,必须在页面销毁时调用取消函数
// 如果不调用SantaLogic 即使退出了,这个闭包还在 Bridge 里,
// 下次分享成功时,代码还会跑,且 SantaLogic 无法被垃圾回收!
_cancelShareListener?.call();
super.onClose();
}
} }
``` ```
## 注意事项 ## 注意事项
1. **单例模式**`FlutterBridge` 使用单例模式,在整个应用中只有一个实例。 1. **单例模式**`FlutterBridge` 使用单例模式,在整个应用中只有一个实例。
2. **监听器管理**:使用 `on()` 注册的监听器在使用完毕后应该使用`_listener1.call()` 取消注册,避免内存泄漏, `off()` 这将移除对应type的所有监听器。`once()`方法不用手动取消监听,内部自动清理监听。 2. **监听器管理**:使用 `on()` 注册的监听器在使用完毕后必须使用`_listener1.call()` 取消注册,避免内存泄漏, `off()` 慎用,这将移除对应type的所有监听器。`sendRequest()`方法是融合了发送消息+监听回调,不用手动取消监听,内部自动清理监听。并且只监听一次,防止多次回调。
3. **消息格式**:所有消息都遵循 `{'type': '消息类型', 'data': {...}}` 的格式。 3. **消息格式**:所有消息都遵循 `{'type': '消息类型', 'data': {...}}` 的格式。

View File

@@ -131,6 +131,15 @@ enum FromFlutterAppEnum {
@JS() @JS()
external void sendMessageToNative(String data); external void sendMessageToNative(String data);
/// 🆕 [新增] 通用请求任务模型
class _RequestTask {
final String sendType; // 发送给 Native 的类型
final Map<String, dynamic> params; // 发送的参数
final Function(Map<String, dynamic>) callback; // 成功回调
_RequestTask(this.sendType, this.params, this.callback);
}
class FlutterBridge { class FlutterBridge {
static final FlutterBridge instance = FlutterBridge._internal(); static final FlutterBridge instance = FlutterBridge._internal();
@@ -139,6 +148,12 @@ class FlutterBridge {
final _messageListeners = <String, List<Function(Map<String, dynamic>)>>{}; final _messageListeners = <String, List<Function(Map<String, dynamic>)>>{};
var textMap = <String, String>{}; //多语言翻译 var textMap = <String, String>{}; //多语言翻译
/// 🆕 [新增] 通用请求队列池 (Key: listenType, Value: 任务列表)
final Map<String, List<_RequestTask>> _requestQueues = {};
/// 🆕 [新增] 忙碌状态池 (记录哪些 listenType 当前正在等待 Native 回复)
final Set<String> _activeResponseTypes = {};
FlutterBridge._internal() { FlutterBridge._internal() {
_initListener(); _initListener();
} }
@@ -193,21 +208,6 @@ class FlutterBridge {
}); });
} }
/// 仅监听一次,触发后自动移除
void once(String type, Function(Map<String, dynamic>) callback) {
// 定义一个引用,用于在回调内部取消自己
VoidCallback? cancelRef;
// 注册监听
cancelRef = on(type, (data) {
// 1. 立即移除监听
cancelRef?.call();
// 2. 执行真正的业务回调
callback(data);
});
}
/// 3. 核心修改on 方法 /// 3. 核心修改on 方法
/// 返回一个 VoidCallback调用它即可取消本次监听 /// 返回一个 VoidCallback调用它即可取消本次监听
VoidCallback on(String type, Function(Map<String, dynamic>) callback) { VoidCallback on(String type, Function(Map<String, dynamic>) callback) {
@@ -236,14 +236,86 @@ class FlutterBridge {
}; };
} }
/// 4. 兼容旧代码的 off 方法 /// 警告:这会移除该类型下的【所有】监听器 (慎用,通常用于全局重置,一旦调用,该 type 别的地方监听器将失效)
/// 警告:这会移除该类型下的【所有】监听器
void off(String type) { void off(String type) {
if (_messageListeners.containsKey(type)) { if (_messageListeners.containsKey(type)) {
_messageListeners.remove(type); _messageListeners.remove(type);
print("All listeners for '$type' have been removed."); 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<String, dynamic> params,
required Function(Map<String, dynamic>) 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 方法) // 发送消息给 App通过 WebView 调用 JS 方法)
void sendToFlutter(String type, Map<String, dynamic> data) { void sendToFlutter(String type, Map<String, dynamic> data) {
@@ -277,8 +349,8 @@ class FlutterBridge {
sendToFlutter(ToFlutterAppEnum.createGame.code, {'gameId': gameId}); sendToFlutter(ToFlutterAppEnum.createGame.code, {'gameId': gameId});
void wantToPlay(String gameId) => void wantToPlay(String gameId) =>
sendToFlutter(ToFlutterAppEnum.wantToPlay.code, {'gameId': gameId}); sendToFlutter(ToFlutterAppEnum.wantToPlay.code, {'gameId': gameId});
void translateRequest(Map<String, String> data) => // void translateRequest(Map<String, String> data) =>
sendToFlutter(ToFlutterAppEnum.translateRequest.code, data); // sendToFlutter(ToFlutterAppEnum.translateRequest.code, data);//移除建议使用sendRequest方法
void toRecharge() => sendToFlutter(ToFlutterAppEnum.toRecharge.code, {}); void toRecharge() => sendToFlutter(ToFlutterAppEnum.toRecharge.code, {});
void toRedDiamond() => sendToFlutter(ToFlutterAppEnum.toRedDiamond.code, {}); void toRedDiamond() => sendToFlutter(ToFlutterAppEnum.toRedDiamond.code, {});
void jumpToH5(String path, String title) => sendToFlutter( void jumpToH5(String path, String title) => sendToFlutter(