如何通过Leak Canary发现内存泄漏?

时间:2019-12-03 15:50:00

标签: java android android-studio memory-leaks

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

enter image description here

关于这件事,很奇怪的是,我没有在使用API​​ 27的虚拟设备上收到此泄漏,但在具有API 29的虚拟设备上以及在我的真实设备上得到了此泄漏。我想提及的下一件事情是,我在所有设备上都运行了Android Studio分析器。在API 27上,只有1个MainActivity实例(以及几个MainActivity $ ...类,我猜这是由startActivity(new Intent(this, AnotherActivity.class))之类的调用导致的)。

在其他设备上,实例数量随设备旋转次数而增加。按下按钮 Force Garbage collection [FGC] 并查看转储的堆时,实例仍然存在,因此这意味着内存泄漏。 BUT (BUT ),在较长时间后再次按下 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();

被调用。由于我在互联网上甚至找不到1条提示,所以我想我必须忍受这个问题...

0 个答案:

没有答案