You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Isolate Handler监听Firebase流仅获一次数据的问题

在Isolate中监听Firebase Realtime Database流仅触发一次的问题解决

问题分析

你的代码存在两个核心问题导致流仅触发一次:

  1. 未保留StreamSubscription引用:每次调用getDriverDB时创建的onValue流订阅没有被持有引用,Dart的垃圾回收机制会回收这个无引用的订阅,导致后续数据库更新无法触发监听。
  2. 重复创建订阅未清理:每次收到新数据时都会创建新的流订阅,但旧的订阅没有被取消,可能导致资源泄漏,同时新订阅也因无引用被回收。

解决方案

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 09:35:14