如何在OnDestroy中移除Firebase监听器以减少内存泄漏?
修复方案
内存泄漏原因
你当前使用的ValueEventListener、ChildEventListener均为匿名内部类,会隐式持有当前Activity的引用。如果Activity销毁时没有主动移除监听器,Firebase SDK会持续持有监听器实例,导致Activity无法被垃圾回收,最终出现内存泄漏。
具体修改步骤
1. 声明监听器和数据库引用为Activity成员变量
在ProductDetailsActivity类的顶部添加如下成员变量,方便后续在onDestroy中访问并移除:
// 产品信息监听器及对应引用 private ValueEventListener mProductValueListener; private DatabaseReference mProductRef; // 图片列表子节点监听器及对应引用 private ChildEventListener mImageChildListener; private DatabaseReference mImageRef;
注:查询配送费使用的
addListenerForSingleValueEvent触发一次回调后会自动解绑,无需手动处理,不会造成内存泄漏。
2. 修改getProductDetails方法,绑定监听器到成员变量
把原来的匿名监听器实例赋值给刚才声明的成员变量,再添加到数据库引用上,同时加Activity状态判断避免销毁后更新UI崩溃:
private void getProductDetails(String productID) { mProductRef = FirebaseDatabase.getInstance().getReference().child("Products").child(productID); mProductValueListener = new ValueEventListener() { @Override public void onDataChange(@NotNull DataSnapshot dataSnapshot) { // 增加状态判断,Activity销毁后不再执行后续逻辑 if (isFinishing() || isDestroyed()) { return; } if (dataSnapshot.exists()){ Products products = dataSnapshot.getValue(Products.class); productName.setText(products.getPname()); productPrice.setText(products.getPrice()); if (products.getInStock().equals("Yes")){ inStock.setText("In Stock"); inStock.setTextColor(ContextCompat.getColor(ProductDetailsActivity.this,R.color.primary)); }else { inStock.setText("Not In Stock"); inStock.setTextColor(ContextCompat.getColor(ProductDetailsActivity.this,R.color.red)); addToCartBtn.setEnabled(false); } ifFreeDelivery = "No"; DatabaseReference mref = FirebaseDatabase.getInstance().getReference("Values").child("deliveryCharge"); mref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { if (isFinishing() || isDestroyed()) return; String phone = dataSnapshot.getValue(String.class); deliveryCharge = Integer.parseInt(phone); } @Override public void onCancelled(DatabaseError databaseError) { } }); Products productsImg = dataSnapshot.getValue(Products.class); ArrayList<String> test = productsImg.getImage(); DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference(); mImageRef = rootRef.child("Products").child(products.getPid()).child("image"); mImageChildListener = new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { if (isFinishing() || isDestroyed()) return; test.add(s); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { if (isFinishing() || isDestroyed()) return; } @Override public void onChildRemoved(DataSnapshot dataSnapshot) { } @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled(DatabaseError databaseError) { } }; mImageRef.addChildEventListener(mImageChildListener); ArrayList<SlideModel> images = new ArrayList<>(); for(int i = 0; i < test.size() ; i++) { images.add(new SlideModel(test.get(i),null)); } productImage.setImageList(images); productImgDownloadBtn.setOnClickListener(v -> { for(int i = 0; i < products.getPicCount() ; i++) { String url = test.get(i).replace(" ",""); DownloadImage(url); } }); productDescription.setText(products.getDescription()); productDetailsCopyBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("Product description",products.getDescription()); clipboard.setPrimaryClip(clip); Toast.makeText(ProductDetailsActivity.this, "Product description copied..", Toast.LENGTH_SHORT).show(); } }); } } @Override public void onCancelled(@NonNull @NotNull DatabaseError databaseError) { } }; mProductRef.addValueEventListener(mProductValueListener); }
3. 重写onDestroy方法,主动移除监听器
在Activity销毁时主动解绑所有监听器,切断Firebase对Activity的引用链:
@Override protected void onDestroy() { super.onDestroy(); // 移除产品信息监听 if (mProductRef != null && mProductValueListener != null) { mProductRef.removeEventListener(mProductValueListener); } // 移除图片列表子节点监听 if (mImageRef != null && mImageChildListener != null) { mImageRef.removeEventListener(mImageChildListener); } }
内容的提问来源于stack exchange,提问作者ucchasharen
相关产品推荐
相关产品推荐

