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

Android Java中Firebase实时数据库节点删除后自动重现问题

问题描述

我正在开发一款Android应用,产品数据可正常插入Firebase实时数据库,单次插入功能正常。但删除数据库中的特定产品节点时,删除代码执行成功的同时,该产品会再次被添加到数据库中。

主Fragment原代码

public class Products_fragment extends Fragment {
    FloatingActionButton fab_addproduct;
    RecyclerView recyclerView;
    EditText searchview;
    BottomSheetDialog bottomSheetDialog;
    FirebaseDatabase firebaseDatabase;
    ArrayList<Product_model> DatabaseProductlist;
    FirebaseAuth auth;
    CircularDotsLoader ProgressBar;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_products_fragment, container, false);

        setviews(view);
        setInitilization();
        setclicks();
        setadapter();

        return view;
    }

    private void setInitilization() {
        DatabaseProductlist = new ArrayList<>();
    }

    private void setadapter() {
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        FirebaseUser currentFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();

        assert currentFirebaseUser != null;
        String UID = currentFirebaseUser.getUid();
        DatabaseReference reference  = database.getReference("Users").child(UID).child("Products");

        reference.addListenerForSingleValueEvent(new ValueEventListener() {
            @SuppressLint("NotifyDataSetChanged")
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                ProgressBar.setVisibility(View.GONE);
                DatabaseProductlist.clear();
                for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                    Product_model model = dataSnapshot.getValue(Product_model.class);
                    DatabaseProductlist.add(model);
                }
                Product_Adapter product_adapter = new Product_Adapter(DatabaseProductlist);
                recyclerView.setAdapter(product_adapter);
                product_adapter.notifyDataSetChanged();

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                ToastUtility.showErrorToast(getContext(), error.getMessage());
            }
        });
    }

    private void setclicks() {

    fab_addproduct.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            bottomSheetDialog = new BottomSheetDialog(getContext(), R.style.AppBottomSheetDialogTheme);
            bottomSheetDialog.setContentView(R.layout.productadd_bottom_sheet);
            TextInputEditText product_id = bottomSheetDialog.findViewById(R.id.product_id);
            TextInputEditText product_name = bottomSheetDialog.findViewById(R.id.product_name);
            TextInputEditText product_quantity = bottomSheetDialog.findViewById(R.id.product_quantity);
            MaterialButton add_btn = bottomSheetDialog.findViewById(R.id.add_btn);
            TextInputEditText product_price = bottomSheetDialog.findViewById(R.id.product_price);

            add_btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (Objects.requireNonNull(product_name.getText()).toString().isEmpty() || product_id.getText().toString().isEmpty() || product_price.getText().toString().isEmpty() || product_quantity.getText().toString().isEmpty()) {
                        ToastUtility.showErrorToast(getContext(), "Please Fill all Fields");
                    } else {


                        Product_model model = new Product_model(Integer.parseInt(product_id.getText().toString()), Integer.parseInt(product_quantity.getText().toString()), Integer.parseInt(product_price.getText().toString()), product_name.getText().toString());
                        firebaseDatabase = FirebaseDatabase.getInstance();
                        DatabaseReference reference = firebaseDatabase.getReference("Users");

                        FirebaseUser currentFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();

                        assert currentFirebaseUser != null;
                        String UID = currentFirebaseUser.getUid();
                        reference.child(UID).child("Products").addListenerForSingleValueEvent(new ValueEventListener() {
                            @Override
                            public void onDataChange(@NonNull DataSnapshot snapshot) {
                                if (snapshot.child(product_name.getText().toString().trim()).exists()) {
                                    ToastUtility.showErrorToast(getContext(),"Product Already Exists!");
                                }
                                else{

                                    reference.child(UID).child("Products").child(product_name.getText().toString().trim()).setValue(model).addOnCompleteListener(new OnCompleteListener<Void>() {
                                        @Override
                                        public void onComplete(@NonNull Task<Void> task) {
                                            if(task.isSuccessful()){
                                                ToastUtility.showSuccessToast(getContext(),"Successfully Added");
                                                setadapter();
                                            }
                                            else{
                                                ToastUtility.showErrorToast(getContext(), Objects.requireNonNull(task.getException()).getMessage());
                                            }
                                        }
                                    });
                                }
                            }

                            @Override
                            public void onCancelled(@NonNull DatabaseError error) {
                                ToastUtility.showErrorToast(getContext(),error.getMessage());
                            }
                        });
                    }
                }
            });

            bottomSheetDialog.show();
        }
    });
    }

    private void setviews(View view) {
        recyclerView = view.findViewById(R.id.recyclerView);
        fab_addproduct = view.findViewById(R.id.fab_addproduct);
        searchview = view.findViewById(R.id.searchview);
        ProgressBar = view.findViewById(R.id.progressbar);
    }
}

Adapter原代码

public class Product_Adapter extends RecyclerView.Adapter<Product_Adapter.myviewholder> {
    ArrayList<Product_model> dataholder;

    public Product_Adapter(ArrayList<Product_model> dataholder) {
        this.dataholder = dataholder;
    }

    @NonNull
    @Override
    public myviewholder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.singlerow_product, parent, false);
        return new myviewholder(view);    }

    @SuppressLint("SetTextI18n")
    @Override
    public void onBindViewHolder(@NonNull myviewholder holder, int position) {
        holder.idtv.setText("" + dataholder.get(position).getId());
        holder.pnametv.setText("" + dataholder.get(position).getProduct_name());
        holder.pqtytv.setText("" + dataholder.get(position).getProduct_quantity());
        holder.ppritv.setText("" + dataholder.get(position).getProduct_price());

        holder.delimg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                FirebaseDatabase db = FirebaseDatabase.getInstance();
                FirebaseUser curUser = FirebaseAuth.getInstance().getCurrentUser();
                assert curUser != null;
                String UID = curUser.getUid();
                String productname = dataholder.get(holder.getAdapterPosition()).getProduct_name();
                DatabaseReference reference  = db.getReference("Users").child(UID).child("Products");

                reference.child(productname).setValue(null).addOnCompleteListener(new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {
                        if(task.isSuccessful())
                        {
                            ToastUtility.showSuccessToast(view.getContext(),"Suucessfully Deleted");
                            notifyItemRemoved(holder.getAdapterPosition());
                        }
                        else
                        {
                            ToastUtility.showErrorToast(view.getContext(),"Product is not deleting ");
                        }

                    }
                });
            }
        });
    }

    @Override
    public int getItemCount() {
        return dataholder.size();
    }

    static class myviewholder extends RecyclerView.ViewHolder{
        TextView idtv, pnametv, pqtytv, ppritv, delimg, edit_img;
        public myviewholder(@NonNull View itemView) {
            super(itemView);
            idtv = itemView.findViewById(R.id.idtv);
            pnametv = itemView.findViewById(R.id.pnametv);
            pqtytv = itemView.findViewById(R.id.pqtytv);
            ppritv = itemView.findViewById(R.id.ppritv);
            delimg = itemView.findViewById(R.id.delimg);
            edit_img = itemView.findViewById(R.id.edit_img);
        }
    }
}

问题根源

  1. 异步检查的时间差:插入产品时使用addListenerForSingleValueEvent异步检查产品是否存在,若用户在检查完成前删除了该产品,监听回调会误判产品不存在,进而执行插入操作导致产品被恢复。
  2. 本地数据源不同步:删除操作仅修改了Firebase数据库,但本地dataholder列表未移除对应产品,UI和数据一致性被破坏。
  3. BottomSheet内存泄漏:未正确销毁BottomSheetDialog,可能导致其持有Fragment引用,触发意外的插入逻辑。

修复方案

1. 原子化插入逻辑(替换原add_btn点击事件)

用Firebase事务实现“检查+插入”的原子操作,避免异步时间差问题:

add_btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (Objects.requireNonNull(product_name.getText()).toString().isEmpty() || product_id.getText().toString().isEmpty() || product_price.getText().toString().isEmpty() || product_quantity.getText().toString().isEmpty()) {
            ToastUtility.showErrorToast(getContext(), "请填写所有字段");
            return;
        }

        String productName = product_name.getText().toString().trim();
        Product_model model = new Product_model(
                Integer.parseInt(product_id.getText().toString()),
                Integer.parseInt(product_quantity.getText().toString()),
                Integer.parseInt(product_price.getText().toString()),
                productName
        );

        FirebaseUser currentFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();
        assert currentFirebaseUser != null;
        String UID = currentFirebaseUser.getUid();
        DatabaseReference productRef = FirebaseDatabase.getInstance().getReference("Users")
                .child(UID).child("Products").child(productName);

        // 使用事务原子化检查并插入
        productRef.runTransaction(new Transaction.Handler() {
            @NonNull
            @Override
            public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
                if (mutableData.getValue() != null) {
                    // 产品已存在,终止事务
                    return Transaction.abort();
                }
                mutableData.setValue(model);
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(@Nullable DatabaseError error, boolean committed, @Nullable DataSnapshot currentData) {
                if (committed) {
                    ToastUtility.showSuccessToast(getContext(), "添加成功");
                    setadapter();
                    bottomSheetDialog.dismiss();
                } else {
                    if (error != null) {
                        ToastUtility.showErrorToast(getContext(), error.getMessage());
                    } else {
                        ToastUtility.showErrorToast(getContext(), "产品已存在");
                    }
                }
            }
        });
    }
});

2. 同步更新本地数据源(修改Adapter删除逻辑)

删除成功后先移除本地数据,再通知UI更新:

holder.delimg.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        FirebaseDatabase db = FirebaseDatabase.getInstance();
        FirebaseUser curUser = FirebaseAuth.getInstance().getCurrentUser();
        assert curUser != null;
        String UID = curUser.getUid();
        int position = holder.getAdapterPosition();
        String productname = dataholder.get(position).getProduct_name();
        DatabaseReference reference = db.getReference("Users").child(UID).child("Products");

        reference.child(productname).removeValue().addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                if(task.isSuccessful()){
                    ToastUtility.showSuccessToast(view.getContext(),"删除成功");
                    // 先移除本地数据源
                    dataholder.remove(position);
                    // 更新UI
                    notifyItemRemoved(position);
                    notifyItemRangeChanged(position, dataholder.size());
                } else {
                    ToastUtility.showErrorToast(view.getContext(),"删除失败");
                }
            }
        });
    }
});

3. 销毁BottomSheet避免泄漏(在主Fragment中添加)

@Override
public void onDestroyView() {
    super.onDestroyView();
    if (bottomSheetDialog != null && bottomSheetDialog.isShowing()) {
        bottomSheetDialog.dismiss();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 03:50:20