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); } } }
问题根源
- 异步检查的时间差:插入产品时使用
addListenerForSingleValueEvent异步检查产品是否存在,若用户在检查完成前删除了该产品,监听回调会误判产品不存在,进而执行插入操作导致产品被恢复。 - 本地数据源不同步:删除操作仅修改了Firebase数据库,但本地
dataholder列表未移除对应产品,UI和数据一致性被破坏。 - 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
相关产品推荐
相关产品推荐

