使用Isolate Handler监听Firebase流仅获一次数据的问题
在Isolate中监听Firebase Realtime Database流仅触发一次的问题解决
问题分析
你的代码存在两个核心问题导致流仅触发一次:
- 未保留StreamSubscription引用:每次调用
getDriverDB时创建的onValue流订阅没有被持有引用,Dart的垃圾回收机制会回收这个无引用的订阅,导致后续数据库更新无法触发监听。 - 重复创建订阅未清理:每次收到新数据时都会创建新的流订阅,但旧的订阅没有被取消,可能导致资源泄漏,同时新订阅也因无引用被回收。
解决方案
在Isolate的入口方法中维护一个全局的StreamSubscription变量,每次创建新的Firebase流订阅前,先取消之前的订阅,同时保留新订阅的引用,防止被GC回收。
修改后的代码
Isolate入口方法(entryPoint)
static void entryPoint(Map<String, dynamic> context) async { await Firebase.initializeApp(); final messenger = HandledIsolate.initialize(context); // 声明全局变量保存当前的流订阅 StreamSubscription<DatabaseEvent>? _driverSubscription; void getDriverDB(List<dynamic> isolateData){ print('===================='); Map<String, dynamic> drivers = {}; GeoHasher geo = GeoHasher(); const double latMultiplier = 0.0144927536231884; const double lonMultiplier = 0.0181818181818182; const double factor = 1.24; double lowerLat, lowerLon, greaterLat, greaterLon; if (isolateData[0].isEmpty && isolateData[1] == false) { lowerLat = isolateData[2] - (latMultiplier * factor); lowerLon = isolateData[3] - (lonMultiplier * factor); greaterLat = isolateData[2] + (latMultiplier * factor); greaterLon = isolateData[3] + (lonMultiplier * factor); } else if (isolateData[0].isNotEmpty && isolateData[1] == true) { lowerLat = isolateData[0]['pick_lat'] - (latMultiplier * factor); lowerLon = isolateData[0]['pick_lng'] - (lonMultiplier * factor); greaterLat = isolateData[0]['pick_lat'] + (latMultiplier * factor); greaterLon = isolateData[0]['pick_lng'] + (lonMultiplier * factor); } else { lowerLat = 0.0; lowerLon = 0.0; greaterLat = 0.0; greaterLon = 0.0; } var lower = geo.encode(lowerLon, lowerLat); var higher = geo.encode(greaterLon, greaterLat); var fdb = FirebaseDatabase.instance .ref('drivers') .orderByChild('g') .startAt(lower) .endAt(higher); // 先取消之前的订阅(如果存在) _driverSubscription?.cancel(); // 创建新订阅并保存引用 _driverSubscription = fdb.onValue.listen((event) { print('<<<<<<<<<<<<>>>>>>>>>>>>'); // 可选:将数据发送回主Isolate处理 messenger.send(event.snapshot.value); }); } messenger.listen((data) { getDriverDB(data); }); }
创建Isolate的方法(保持原代码不变)
getDrivers() { List<dynamic> data = []; bool isAddressListEmpty = addressList.isEmpty; data.addAll([userRequestData, isAddressListEmpty]); if(!isAddressListEmpty){ var pickupLocation = addressList.firstWhere((element) => element.type == 'pickup').latlng; double pickupLat = pickupLocation.latitude; double pickupLong = pickupLocation.longitude; data.addAll([pickupLat, pickupLong]); } return isolates.spawn<Map<String, dynamic>>(entryPoint, name: "counter", onReceive: setCounter, onInitialized: () => isolates.send(data, to: "counter")); }
额外注意事项
- 必须在Isolate内部完成
Firebase.initializeApp()初始化,不能依赖主Isolate的Firebase实例,你的代码已经正确实现这一点。 - 如果需要在Isolate销毁前清理资源,可以监听Isolate的退出事件,主动取消订阅避免资源泄漏。
内容的提问来源于stack exchange,提问作者Sheldon Osotsi
相关产品推荐
相关产品推荐

