Java 如何使用 Leak Canary

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33654503/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:52:55  来源:igfitidea点击:

How to use Leak Canary

javaandroidmemory-leaksleakcanary

提问by Charas

I know this is probably a dumb question, but I am pretty new at developing android, and I currently experiencing an OutOfMemoryError in my apps, which I have tried to debug using MAT, but it is still too hard to find the leak in a few activities, then I found LeakCanary, which seems simpler and easier to use, however I could not find any beginner step by step guide on using Leak Canary, even on Google. I have installed LeakCanary through the dependencies in my build.gradle, and this is what I got so far :

我知道这可能是一个愚蠢的问题,但我在开发 android 方面还很陌生,而且我目前在我的应用程序中遇到了 OutOfMemoryError,我曾尝试使用 MAT 对其进行调试,但仍然很难找到一些漏洞活动,然后我找到了 LeakCanary,它看起来更简单,更容易使用,但是我什至在谷歌上也找不到任何关于使用 Leak Canary 的初学者分步指南。我已经通过 build.gradle 中的依赖项安装了 LeakCanary,这是我目前得到的:

ExampleApplication.java

示例应用程序.java

public class ExampleApplication extends Application {

    public static RefWatcher getRefWatcher(Context context) {
        ExampleApplication application = (ExampleApplication) context.getApplicationContext();
        return application.refWatcher;
    }

    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = LeakCanary.install(this);
    }

    final class KeyedWeakReference extends WeakReference<Object> {
        public final String key;
        public final String name;

        KeyedWeakReference(Object referent, String key, String name,
                       ReferenceQueue<Object> referenceQueue) {
            super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
            this.key = checkNotNull(key, "key");
            this.name = checkNotNull(name, "name");
        }
    }

    public void watch(Object watchedReference, String referenceName) {
        checkNotNull(watchedReference, "watchReference");
        checkNotNull(referenceName, "referenceName");
        if(debuggerControl.isDebuggerAttached()) {
            return;
        }
        final long watchStartNanoTime = System.nanoTime();
        String key = UUID.randomUUID().toString();
        retainedKeys.add(key);
        final KeyedWeakReference reference =
            new KeyedWeakReference(watchedReference, key, referenceName, queue);
        watchExecutor.execute()

    }
}

Let's say I have an Activity where I want LeakCanary to watch an object

假设我有一个活动,我希望 LeakCanary 观察一个对象

SampleActivity.java

示例活动.java

public class SampleActivity extends Activity implements View.OnClickListener {
    ImageView level001, level002;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.choose_level);

        level001 = (ImageView) findViewById(R.id.level001);
        level002 = (ImageView) findViewById(R.id.level002);

        // Do all kinds of function
        // How do I use LeakCanary to watch these objects ?

    }
}

Now how do I use LeakCanary to see which object is causing the memory leak. Thank you for your help and assistance.

现在我如何使用 LeakCanary 来查看导致内存泄漏的对象。感谢您的帮助和协助。

回答by Budius

The nice thing about leak canary is how automated it works. By default, it already "watches" for activities that are not being properly GCed. So out of the box, if any activity is leaking you should receive the notification.

泄漏金丝雀的好处在于它的工作方式是自动化的。默认情况下,它已经“监视”未正确 GC 的活动。所以开箱即用,如果有任何活动泄漏,您应该收到通知。

On my project I've added an extra method on the Applicationlike this:

在我的项目中,我添加了一个额外的方法,Application如下所示:

public class ExampleApplication extends Application {
    public static ExampleApplication instance;
    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        refWatcher = LeakCanary.install(this);
    }

    public void mustDie(Object object) {
        if (refWatcher != null) {
            refWatcher.watch(object);
        }
    }
}

so the important stuff with garbage collection and memory leak and canary is to know when stuff should be collected and ask that item to be watched.

所以垃圾收集、内存泄漏和金丝雀的重要内容是知道什么时候应该收集东西并要求监视该项目。

For for example we're using a "base fragment" with the following code:

例如,我们使用带有以下代码的“基本片段”:

@Override
public void onDestroy() {
    super.onDestroy();
    ExampleApplication.instance.mustDie(this);
}

this way LeakCanaryis trying to check if any fragment is leaking memory.

这种方式LeakCanary试图检查是否有任何片段正在泄漏内存。

So for you to further implement on your app, you could/should on tasks or instances that you know it should be garbage collected but you think it might not be, and you're not sure where, you can call that too: ExampleApplication.instance.mustDie(object);

因此,为了进一步在您的应用程序上实现,您可以/应该在您知道它应该被垃圾收集但您认为它可能不是,并且您不确定在哪里的任务或实例上,您也可以调用它: ExampleApplication.instance.mustDie(object);

and then you MUST run the application and rotate the device and force the leak to happen, so leak canary can grab/analyse the stack trace and give you valuable information on how to fix it.

然后您必须运行应用程序并旋转设备并强制发生泄漏,因此泄漏金丝雀可以抓取/分析堆栈跟踪并为您提供有关如何修复它的宝贵信息。

I hope it helps.

我希望它有帮助。

回答by Nilesh Panchal

I used here in Application

我在应用程序中使用过

import android.content.Context;
import android.support.multidex.MultiDex;
import android.support.multidex.MultiDexApplication;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
import school.my.com.school.BuildConfig;

public class AppController extends MultiDexApplication {

private RefWatcher refWatcher;

public static RefWatcher getRefWatcher(Context context) {
    AppController application = (AppController) context.getApplicationContext();
    return application.refWatcher;
}



@Override
public void onCreate() {
    super.onCreate();
    if(BuildConfig.DEBUG)
        refWatcher = LeakCanary.install(this);

}

}

You can use here Application in place of MultiDexApplication

您可以在此处使用 Application 代替 MultiDexApplication

回答by Michael Osofsky

I had the same question about how to use LeakCanary. I just wanted to see a basic example of how to launch it and see my first path to a leaked object.

我对如何使用 LeakCanary 有同样的问题。我只是想看看如何启动它的基本示例,并查看我的第一个泄漏对象路径。

How to Use LeakCanary

如何使用 LeakCanary

Here is a basic example of how to LeakCanary working:

以下是 LeakCanary 如何工作的基本示例:

How to Use LeakCanary(4 minutes 13 seconds)

如何使用 LeakCanary(4 分 13 秒)

One of the issues I had to overcome was figuring out that I had to launch the app in regular run-mode as opposed to debug mode. I also had to deviate from the basic instructions and set my application-level build.gradlefile as follows:

我必须克服的问题之一是弄清楚我必须在常规运行模式而不是调试模式下启动应用程序。我还不得不偏离基本说明并将我的应用程序级build.gradle文件设置如下:

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
  releaseImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
}

I think debugImplementationdidn't work for me because LeakCanary ignores leak detection when debugging. Although LeakCanary says it provides the "no-op" version of its library for release, since debug didn't work for me, I changed the releaseImplementationabove from the recommended com.squareup.leakcanary:leakcanary-android-no-op:1.5.4to com.squareup.leakcanary:leakcanary-android:1.5.4.

我认为debugImplementation这对我不起作用,因为LeakCanary 在调试时忽略了泄漏检测。尽管 LeakCanary 表示它提供了其库“无操作”版本以供发布,但由于调试对我不起作用,我将releaseImplementation上述内容从推荐com.squareup.leakcanary:leakcanary-android-no-op:1.5.4com.squareup.leakcanary:leakcanary-android:1.5.4.

As you can see in the video, another issue I had to deal with was giving write-access to LeakCanary. I swiped down and saw a notification from LeakCanary saying it had failed to obtain write-access (more info). I never saw the permission request. So in one case (not shown in video), I gave my application write access by going to the Settings app, finding my app (as opposed to the app called "Leak" that LeakCanary installs), and turning on write-access. In the video I didn't need to do that because I gave permission by responding to the notification. Then while using my app I check periodically for new notifications (by swiping down). I saw messages like "XYZActivity leaked 217 KB". I tapped on that and it took me into that Leak app.

正如您在视频中看到的,我必须处理的另一个问题是授予对 LeakCanary 的写访问权限。我向下滑动并看到来自 LeakCanary 的通知,说它无法获得写访问权限(更多信息)。我从来没有看到许可请求。因此,在一种情况下(视频中未显示),我通过转到“设置”应用程序,找到我的应用程序(而不是 LeakCanary 安装的名为“Leak”的应用程序)并打开写访问权限来授予我的应用程序写访问权限。在视频中,我不需要这样做,因为我通过回复通知给予了许可。然后在使用我的应用程序时,我会定期检查新通知(通过向下滑动)。我看到诸如“XYZActivity 泄露 217 KB”之类的消息。我点击了它,它把我带到了那个 Leak 应用程序。

Also, I noticed that Sometimes it could take up to a few minutes for the analysis to complete and show a memory leak notification on your phone.

此外,我注意到有时可能需要几分钟才能完成分析并在您的手机上显示内存泄漏通知

How to verify a memory leak fix using LeakCanary

如何使用 LeakCanary 验证内存泄漏修复

Now that I've fixed some of my memory leaks, I use LeakCanary to verify the fix. However, if LeakCanary doesn't report anything I don't necessarily believe that's because my leak is fixed. It could just be LeakCanary is broken.

现在我已经修复了一些内存泄漏,我使用 LeakCanary 来验证修复。但是,如果 LeakCanary 没有报告任何内容,我不一定相信那是因为我的泄漏已修复。可能只是 LeakCanary 坏了。

How to verify a memory leak fix using LeakCanary(16 minutes 34 seconds)

如何使用 LeakCanary 验证内存泄漏修复(16 分 34 秒)

Process for Verifying Memory Leak with LeakCanary: 1. Confirm memory leak exists using LeakCanary 2. Fix memory leak and confirm LeakCanary reports no leaks 3. Revert your fix and confirm LeakCanary reports the leak again

使用 LeakCanary 验证内存泄漏的过程: 1. 使用 LeakCanary 确认内存泄漏 2. 修复内存泄漏并确认 LeakCanary 没有报告泄漏 3. 恢复您的修复并确认 LeakCanary 再次报告泄漏

Since LeakCanary shows very little status information when it is working, it's hard to know if it's doing anything at all. This has led me to think I had fixed a memory leak when, in fact, I hadn't. The three steps above are the best way I've found to verify a memory leak fix using LeakCanary.

由于 LeakCanary 在工作时显示的状态信息非常少,因此很难知道它是否正在执行任何操作。这让我以为我已经修复了内存泄漏,但实际上我没有。以上三个步骤是我发现使用 LeakCanary 验证内存泄漏修复的最佳方法。

回答by 100RaBH

I used Leak-Canary like below:

我使用 Leak-Canary 如下:

1) Gradle dependency:

1)Gradle依赖:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.4'

2) Application class:

2)应用类:

   @Override
public void onCreate() {
    super.onCreate();
    if (BuildConfig.DEBUG) {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectCustomSlowCalls()
                .detectNetwork()
                .penaltyLog()
                .penaltyDeath()
                .build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectActivityLeaks()
                .detectLeakedClosableObjects()
                .detectLeakedRegistrationObjects()
                .detectLeakedSqlLiteObjects()
                .penaltyLog()
                .penaltyDeath()
                .build());

        LeakLoggerService.setupLeakCanary(this);
    }
}

3) LeakLoggerService class: place this class in debug package created by gradle.

3)LeakLoggerService类:将这个类放在gradle创建的调试包中。

public class LeakLoggerService extends DisplayLeakService {

public static void setupLeakCanary(Application application) {
    if (LeakCanary.isInAnalyzerProcess(application)) {
        // This process is dedicated to LeakCanary for heap analysis.
        // You should not init your app in this process.
        return;
    }
    LeakCanary.install(application, LeakLoggerService.class,
            AndroidExcludedRefs.createAppDefaults().build());

}

@Override
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
    if (!result.leakFound || result.excludedLeak) {
        return;
    }

    Log.w("LeakCanary", leakInfo);
}

4) Register service to manifest file and 1 permission:

4) 将服务注册到清单文件和 1 个权限:

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  <service android:name=".LeakLoggerService" />

5) Finally verify if setup is successful: Leak an activity ;)

5) 最后验证设置是否成功:Leak an Activity ;)

public class Main2Activity extends AppCompatActivity {
static TextView label;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    label = new TextView(this);
    label.setText("asds");

    setContentView(label);
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 
}
}

kill this activity and check logs with tag : LeakCanary

终止此活动并检查带有标记的日志:LeakCanary

It should work...

它应该工作...

回答by Prakash

Posting this answer because all other answers are no longer needed and there's a better way to find leaks on your app using Leak Canary 2.

发布此答案是因为不再需要所有其他答案,并且有一种使用 Leak Canary 2 查找应用程序泄漏的更好方法。

Just add below dependency to your gradle. No more code changed needed.

只需将以下依赖项添加到您的 gradle 中即可。不再需要更改代码。

debugImplementation 'com.squareup.leakcananry:leakcanary-android:2.2'