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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 00:45:03