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

安卓Firebase实时数据库更新删除触发无限循环异常

问题根因

数据无限循环、应用卡死是Firebase实时数据库监听器的错误用法导致,和字段本身无关,班次时间、票价字段之前能更新只是测试时刚好输入值和旧值一致,未触发数据变更回调,没有进入死循环而已。
核心错误点:

  • EditBus.java中,你在更新按钮的点击事件里给当前巴士节点绑定了ValueEventListener,该监听器会在监听节点的任何数据发生变更时立刻触发onDataChange回调,而你在回调内直接执行updateChildren写操作,写操作又会触发监听器回调,回调再次触发写操作,直接形成无限触发的死循环。
  • 每次点击更新按钮都会新增一个从未移除的ValueEventListener,点击次数越多,并发触发的回调越多,应用卡死速度越快。
  • AddBus.java中存在完全相同的逻辑错误:在点击事件里给Bus根节点绑定永久监听器,回调内执行setValue写操作,同样会触发无限循环;同时该文件存在笔误,引用了未定义的timingsEdit变量,正常情况下无法通过编译。
  • 你当前以巴士编号作为节点ID存储数据,修改巴士编号时仅更新节点内的busNum字段,不会同步修改节点存储路径,会导致数据ID和实际值不匹配的问题。
修复方案

所有写操作(新增、更新、删除)不需要绑定ValueEventListener,直接调用Firebase提供的写方法,通过写操作自带的完成回调处理结果即可。

1. 修复EditBus.java

替换原有更新按钮点击逻辑和删除方法,增加非空校验、操作完成回调,避免重复跳转:

updateButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        String busNumVar= busNumEdit.getText().toString().trim();
        String srcVar=srcEdit.getText().toString().trim();
        String destVar=destEdit.getText().toString().trim();
        String timingsVar=timingsEdit.getText().toString().trim();
        String priceVar=priceEdit.getText().toString().trim();

        if(busNumVar.isEmpty() || srcVar.isEmpty() || destVar.isEmpty() || timingsVar.isEmpty() || priceVar.isEmpty()){
            Toast.makeText(Edit_Bus.this, "所有字段不能为空", Toast.LENGTH_SHORT).show();
            return;
        }

        Map<String,Object> map=new HashMap<>();
        map.put("busNum", busNumVar);
        map.put("src",srcVar);
        map.put("dest",destVar);
        map.put("timings",timingsVar);
        map.put("price",priceVar);
        map.put("busID",busNumVar);

        // 如果你需要支持修改巴士编号作为主键,替换下面的更新逻辑为:先在新ID节点写入数据,写入成功后删除旧ID节点
        databaseReference.updateChildren(map).addOnCompleteListener(task -> {
            if(task.isSuccessful()){
                Toast.makeText(Edit_Bus.this, "班次更新成功", Toast.LENGTH_SHORT).show();
                startActivity(new Intent(Edit_Bus.this,MainActivity2.class));
                finish();
            }else{
                Toast.makeText(Edit_Bus.this, "更新失败:"+task.getException().getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }
});

private void deleteBus(){
    databaseReference.removeValue().addOnCompleteListener(task -> {
        if(task.isSuccessful()){
            Toast.makeText(this, "班次已删除", Toast.LENGTH_SHORT).show();
            startActivity(new Intent(Edit_Bus.this,MainActivity2.class));
            finish();
        }else{
            Toast.makeText(this, "删除失败:"+task.getException().getMessage(), Toast.LENGTH_SHORT).show();
        }
    });
}

注意:如果你的业务逻辑允许修改巴士编号,不能直接用updateChildren更新,需要先获取旧节点的完整数据,写入到以新巴士编号为ID的节点下,写入成功后再删除旧ID节点,否则会出现节点ID和数据内busNum字段不一致的问题。

2. 修复AddBus.java

移除多余的监听器,修复变量名笔误,增加非空校验:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // 修复笔误:替换不存在的timingsEdit为已定义的timings变量
        String busNumVar=busNum.getText().toString().trim();
        String srcVar=src.getText().toString().trim();
        String destVar=dest.getText().toString().trim();
        String timingsVar=timings.getText().toString().trim();
        String priceVar=price.getText().toString().trim();

        if(busNumVar.isEmpty() || srcVar.isEmpty() || destVar.isEmpty() || timingsVar.isEmpty() || priceVar.isEmpty()){
            Toast.makeText(Add_Bus.this, "所有字段不能为空", Toast.LENGTH_SHORT).show();
            return;
        }

        BusID=busNumVar;
        BusRVModel busRVModel=new BusRVModel(busNumVar,srcVar,destVar,timingsVar,priceVar,BusID);
        databaseReference.child(BusID).setValue(busRVModel).addOnCompleteListener(task -> {
            if(task.isSuccessful()){
                Toast.makeText(Add_Bus.this, "班次添加成功", Toast.LENGTH_SHORT).show();
                startActivity(new Intent(Add_Bus.this,MainActivity2.class));
                finish();
            }else{
                Toast.makeText(Add_Bus.this, "添加失败:"+task.getException().getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }
});

3. 开发注意事项

  • ValueEventListener属于永久监听器,只要不主动调用removeEventListener移除,会一直监听对应节点的数据变化,绝对不要在该监听器的onDataChange回调中执行对同一节点的写操作,否则必然触发无限循环。
  • 如果需要用监听器更新UI,务必在Activity的onStop生命周期中移除已绑定的监听器,避免内存泄漏和后台无效回调触发。
  • Firebase所有写操作都是异步执行,不要在调用写方法后立刻执行页面跳转等逻辑,要在操作完成回调中根据执行结果做后续处理,避免数据未写入完成就跳转导致的数据不一致。

内容的提问来源于stack exchange,提问作者CypherZ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 10:09:20