In App Billing BroadcastReceiver Memory Leak
Asked Answered
P

1

11

I am opening a Activity with ActivityResult and after buying a item successfully, I am closing the current activity which holds purching process and send data back. But Leak Canary catch a memory leak about BillingBroadcastReceiver. I init billing client OnCreate and release onDestroy.

Here is my init method which called in OnCreate

billingClient = BillingClient.newBuilder(this).setListener(this).build();
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(int responseCode) {

                if (responseCode == BillingClient.BillingResponse.OK) {
                    // The billing client is ready. You can query purchases here.
                    loadProducts();
                } else {
                    // Error
                }

            }

            @Override
            public void onBillingServiceDisconnected() {
                // Try to restart the connection on the next request to
                Timber.d("Connection Error");
            }
        });

Load Product infos when billingClient is ready

private void loadProducts() {

if (billingClient.isReady()) {

    List<String> skuList = new ArrayList<>(getViewModel().getSkuIdList());
    SkuDetailsParams params = SkuDetailsParams.newBuilder().setSkusList(skuList).setType(BillingClient.SkuType.INAPP).build();

    billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {

            if (responseCode == BillingClient.BillingResponse.OK) {
                Timber.d("SkuList --> %s", skuDetailsList.size());

            } else {
                Timber.d("Can't querySkuDetailsAsync, responseCode: %s", responseCode);
            }

        }
    });

} else {
    Timber.d("Billing Client not Ready");
}
}

Here is my release method which called in OnDestroy

    if (billingClient != null && billingClient.isReady()) {
        billingClient.endConnection();
        billingClient = null;
    }

OnPurchaseUpdated I made a service call and close this activity based on service result.

public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {

    if (responseCode == BillingClient.BillingResponse.OK && purchases != null) { 
        for (Purchase purchase : purchases) {
            billingClient.consumeAsync(purchase.getPurchaseToken(), new ConsumeResponseListener() {
                @Override
                public void onConsumeResponse(int responseCode, String purchaseToken) {
                    if (responseCode == BillingClient.BillingResponse.OK && purchaseToken != null) {
                        Timber.d("onConsumeResponse --> %s", purchaseToken);
                        getViewModel().informPurchase(necessary data);
                    }
                }
            });
        }
    } else if (responseCode == BillingClient.BillingResponse.USER_CANCELED) {
        // Handle an error caused by a user canceling the purchase flow.
        Timber.d("Billing Cancelled");

    } else {
        Timber.d("An Error Occured");
    }
}

I am using latest library for in app purchase

implementation 'com.android.billingclient:billing:1.2.1'

After buying a item successfully and close recent activity, Leak Canary shows me this error. How can I avoid this memory leak?

BroadcastError

Pina answered 15/3, 2019 at 10:9 Comment(9)
looks like endConnection is not removing the reference to the activity that setListener(this) is setting. Have you verified that billingClient.endConnection(); is actually being called by setting a breakpoint?Bertsche
I checked by setting breakpoint, billingClient.endConnection(); called normally. I also call endConnection method in observer of informPurchase method for test purpose, still the same leak happens @CésarDeLaVegaPina
Also add your broadcastmanager classCauley
Problem is here @ShwetaChauhan, I do not have a boardcastmanager class, it is billing library's classPina
@Pina have you found a solution?Protist
@Protist unfortunately not :(Pina
@Pina Hey, man. Have you found a solution for this? I'm using version 3.0.2 and the problem still persists.Inductile
Nope, They said that, issue has been fixed. It shall be rolled out with the next release. You can check out this issuePina
Feb 5th 2021 and the issue is still not fixed.Dyanna
B
0

The Activity is being destroyed before the billingClient is ready so billingClient.isReady will be false and the endConnection will never be called, so the reference to the Activity (the Activity is the listener of the BillingClient) won't be removed and you will have a leak.

To fix, just do:

if (billingClient != null) {
        billingClient.endConnection();
        billingClient = null;
}
Bertsche answered 20/3, 2019 at 0:29 Comment(1)
Thanks for your answer but it did not work. billingClient is always ready and billingClient.endConnection(); called properly, I double checked with debug. I also checked with removing isReady and result is still samePina

© 2022 - 2024 — McMap. All rights reserved.