首页 > 解决方案 > 如何使用 Leak Canary 找到我的内存泄漏?

问题描述

我在我的 Android Studio 项目中实现了 Leak Canary,在调试模式下运行应用程序并旋转屏幕(MainActivity)几次以检查内存泄漏。Leak Canary 向我展示了以下屏幕:

在此处输入图像描述

奇怪的是,我没有在具有 API 27 的虚拟设备上得到这种泄漏,但我在具有 API 29 的虚拟设备和我的真实设备上得到它。接下来我要提到的是,我在所有设备上运行了 Android Studio 分析器。在 API 27 上,只有 1 个 MainActivity 实例(以及几个 MainActivity$... 类,我猜它们是由类似的调用引起的startActivity(new Intent(this, AnotherActivity.class)))。

在其他设备上,实例数量随着设备旋转次数的增加而增加。当点击Force Garbage collection [FGC]按钮并查看转储堆时,实例仍然存在,因此这意味着存在内存泄漏。但是当较长时间后再次点击FGC按钮然后查看转储堆时,MainActivity 的重复实例消失了,只剩下一个实例。

这种行为怎么会发生?

现在,关于我的代码:

PurchasesHandler 类包含处理 Google Play 购买的必要代码 ( https://developer.android.com/google/play/billing/billing_library_overview )。由于整个类包含大量代码,因此我将展示包含对 MainActivity 或类本身的引用的部分。

public class PurchasesHandler implements PurchasesUpdatedListener, AcknowledgePurchaseResponseListener {
    private Activity activity;
    private BillingClient billingClient;
    private boolean adsRemoved;
    private ReviewedPurchasesCallback reviewedPurchasesCallback;


    public PurchasesHandler(Activity activity, boolean adsRemoved, ReviewedPurchasesCallback reviewedPurchasesCallback) {
        this.activity = activity;
        this.adsRemoved = adsRemoved;
        this.reviewedPurchasesCallback = reviewedPurchasesCallback;
    }

    public interface ReviewedPurchasesCallback {
        void fun1();
        void fun2();
    }

    public void restorePurchases() {
        billingClient = BillingClient.newBuilder(context)
                .enablePendingPurchases()
                .setListener(this)
                .build();
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                    Purchase.PurchasesResult purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
                    List<Purchase> purchaseList = purchasesResult.getPurchasesList();
                    if (purchaseList != null && purchaseList.size() > 0) {
                        for (Purchase purchase : purchaseList) {
                                handlePurchase(purchase);
                        }
                    }
            }

            @Override
            public void onBillingServiceDisconnected() {
                reviewedPurchasesCallback.fun1();
            }
        });

    }

    private void handlePurchase(Purchase purchase) {
        if (something...) {
            reviewedPurchasesCallback.fun2();
            if (purchase.isAcknowledged()) {
                Log.i(...);
            } else {
                AcknowledgePurchaseParams acknowledgePurchaseParams =
                        AcknowledgePurchaseParams.newBuilder()
                                .setPurchaseToken(purchase.getPurchaseToken())
                                .build();
                billingClient.acknowledgePurchase(acknowledgePurchaseParams, this);
            }
        } else if (something...) {
            reviewedPurchasesCallback.fun1();
        }
    }

    @Override
    public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
        Log.i(...);
    }

    public void purchaseAdsRemoval() {
        billingClient = BillingClient.newBuilder(context)
                .enablePendingPurchases()
                .setListener(this)
                .build();
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                    ...
                    billingClient.querySkuDetailsAsync(params.build(),
                            new SkuDetailsResponseListener() {
                                @Override
                                public void onSkuDetailsResponse(BillingResult billingResult,
                                                                 List<SkuDetails> skuDetailsList) {
                                        SkuDetails skuDetail = skuDetailsList.get(0);
                                        if (sku.equals(something...)) {
                                            BillingFlowParams flowParams = BillingFlowParams.newBuilder()
                                                    .setSkuDetails(skuDetail)
                                                    .build();
                                            billingClient.launchBillingFlow(activity, flowParams);
                                        }
                                }
                            });
                    ...
            }

            @Override
            public void onBillingServiceDisconnected() {
                showBillingResponseDialog(...);
            }
        });
    }

    @Override
    public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
            showBillingResponseDialog(...);
            for (Purchase purchase : purchases) {
                handlePurchase(purchase);
            }
    }



    private void showBillingError(BillingResult billingResult) {
        switch (billingResult.getResponseCode()) {
            case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED:
                showBillingResponseDialog(0, resources.getString(...));
                break;
            ...
        }
    }

    private void showBillingResponseDialog(int titleCode, String message) {
        ...
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        ...
    }

    public void unregisterBillingListener() {
        if (billingClient != null) {
            billingClient.endConnection();
            billingClient = null;
        }
    }
}

在 MainActivity 中,该类的实现如下:

PurchasesHandler purchasesHandler = new PurchasesHandler(MainActivity.this, adsRemoved,
                                                new PurchasesHandler.ReviewedPurchasesCallback() {
            @Override
            public void adsRemovalNotPurchased() {
                loadConsentOrRetrieve();
            }

            @Override
            public void adsRemovalPurchased() {
                saveAdsRemovePref();
            }
        });

您认为 PurchasesHandler 与内存泄漏有关吗?有人知道如何找到泄漏的原因吗?也许是一种策略或什么?

编辑

差点忘了最重要的事情。在 MainActivity 我还调用:

@Override
    protected void onPause() {
        super.onPause();
        purchasesHandler.unregisterBillingListener();
        purchasesHandler = null;
    }

编辑 2

通过逐段删除和添加代码并每次运行分析器,我发现是侦听器导致了问题。

billingClient = BillingClient.newBuilder(context)
                .enablePendingPurchases()
                .setListener(this)

似乎这个听众没有被暂停,当

billingClient.endConnection();

叫做。由于我在互联网上什至没有找到任何关于此的提示,我想我必须忍受这个问题......

标签: javaandroidandroid-studiomemory-leaks

解决方案


推荐阅读