java - 如何使用 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();
叫做。由于我在互联网上什至没有找到任何关于此的提示,我想我必须忍受这个问题......
解决方案
推荐阅读
- python - 将 Python 数据发送到 PHP 脚本
- listbox - 使用文件中每一行的文本文件第一个索引填充列表框
- android - RecyclerView 和 ArrayList - 如何从数据库中添加用户图像?
- cobol - COBOL-85 源文件的未知确切格式
- c# - Serializable Attribute 导致 API 使用 ReadAsAsync 返回 null 属性
- c++ - C++ 锁定作用域块的代码执行
- c# - 在 AutoMapper 6 中创建自定义成员解析器实例时出错
- cross-validation - 使用 H2OGradientBoostingEstimator 避免过拟合
- linux - socat 作为客户端必须重新连接到服务器并继续使用相同的 pty 文件
- scons - 如何为所有 SCons 构建变体设置重复 = 0,而不在每个 SConscript 调用中多次重复该选项?