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

Firebase Realtime Database首次调用addValueEventListener失效致崩溃

问题描述

我想获取Firebase数据库中子节点的数量,将该数量作为键以提升数据库可读性。但点击按钮插入新数据时,reference2.child(Rname).addValueEventListener方法未正常工作,导致ordercnt[0]为空,执行reference2.child(Rname).child(ordercnt[0]).child(Integer.toString(i)).setValue(order)时因子路径为空引发崩溃。

相关代码

Cart_submitBtn = view.findViewById(R.id.Cart_submitBtn);
       Cart_submitBtn.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {                
               String Rname = selected.get(0).getRName();

               reference2.child(Rname).addValueEventListener(new ValueEventListener() {
                   @Override
                   public void onDataChange(@NonNull DataSnapshot snapshot) {
                       if(snapshot.exists()){
                           ordercnt[0] = Long.toString(snapshot.getChildrenCount());
                       }
                       else{ ordercnt[0] = "0"; }
                   }

                   @Override
                   public void onCancelled(@NonNull DatabaseError error) {

                   }
               });                
               reference1.child(mAuth.getCurrentUser().getUid()).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
                   @Override
                   public void onComplete(@NonNull Task<DataSnapshot> task) {
                       User user = task.getResult().getValue(User.class);
                       if(user != null){                            
                           for(int i = 0; i < selected.size(); i++) {
                               String order = "";
                               
                               order += "餐點名稱: " + selected.get(i).getsName() + " ";
                               order += selected.get(i).getsCnt() + "份";
                               order += selected.get(i).getsPrice() + "元 ";                              
                               reference2.child(Rname).child(ordercnt[0]).child(Integer.toString(i)).setValue(order);
                           }                                                        
                       }
                   }
               });
           }
       });

报错情况

  • 触发NullPointerException,提示"Can't pass null for argument 'pathString' in child()"
  • 崩溃栈指向调用child(ordercnt[0])的代码行

解决方案

问题根源

Firebase数据库操作是异步执行的,addValueEventListener和get().addOnCompleteListener会在后台线程完成,主线程会直接继续执行后续代码,导致写入数据时ordercnt[0]还未被赋值,最终因空值引发崩溃。

修复代码

将数据写入逻辑嵌套到异步回调中,确保拿到子节点数量和用户信息后再执行写入:

Cart_submitBtn = view.findViewById(R.id.Cart_submitBtn);
Cart_submitBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {                
        String Rname = selected.get(0).getRName();

        // 先获取用户信息
        reference1.child(mAuth.getCurrentUser().getUid()).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<DataSnapshot> task) {
                User user = task.getResult().getValue(User.class);
                if(user != null){
                    // 获取用户信息后,一次性获取子节点数量(无需实时监听)
                    reference2.child(Rname).addListenerForSingleValueEvent(new ValueEventListener() {
                        @Override
                        public void onDataChange(@NonNull DataSnapshot snapshot) {
                            String orderCount = snapshot.exists() ? 
                                Long.toString(snapshot.getChildrenCount()) : "0";
                            
                            // 拿到计数后执行写入
                            for(int i = 0; i < selected.size(); i++) {
                                String order = String.format("餐點名稱: %s %d份 %d元 ",
                                    selected.get(i).getsName(),
                                    selected.get(i).getsCnt(),
                                    selected.get(i).getsPrice());
                              
                                reference2.child(Rname).child(orderCount).child(Integer.toString(i)).setValue(order);
                            }
                        }

                        @Override
                        public void onCancelled(@NonNull DatabaseError error) {
                            // 添加错误日志,方便排查
                            Log.e("FirebaseError", "获取子节点数量失败: " + error.getMessage());
                        }
                    });
                }
            }
        });
    }
});

优化建议

  1. 替换监听方法:用addListenerForSingleValueEvent代替addValueEventListener,仅获取一次数据,避免不必要的实时监听。
  2. 移除数组传值:直接在回调中使用局部变量传递计数,避免空指针风险。
  3. 优化字符串拼接:用String.format替代多次+=,代码更简洁高效。
  4. 添加错误处理:在onCancelled中记录错误日志,便于排查问题。
  5. 考虑自动生成键:如果仅需要唯一订单标识,推荐使用Firebase的push()方法生成唯一键,避免多用户同时写入时的计数冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 13:36:32