我在Android Studio项目中实现了Leak Canary,以调试模式运行该应用程序,并旋转了几次屏幕(MainActivity)以检查内存泄漏。漏金丝雀向我显示了以下屏幕:
关于这件事,很奇怪的是,我没有在使用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;
}
通过逐段删除和添加代码并每次运行探查器,我发现侦听器是造成此问题的原因。
billingClient = BillingClient.newBuilder(context)
.enablePendingPurchases()
.setListener(this)
该监听器似乎并没有在以下时间被暂停
billingClient.endConnection();
被调用。由于我在互联网上甚至找不到1条提示,所以我想我必须忍受这个问题...