如何使用泄漏金丝雀

时间:2015-11-11 15:56:07

标签: java android memory-leaks leakcanary

我知道这可能是一个愚蠢的问题,但我是开发android的新手,我目前在我的应用程序中遇到了OutOfMemoryError,我试图使用MAT调试,但是仍然很难找到泄漏在一些活动中,我找到了LeakCanary,它看起来更简单,更容易使用,但是我找不到任何有关使用Leak Canary的初学者一步一步指南,即使在Google上也是如此。我已经通过build.gradle中的依赖项安装了LeakCanary,这是我到目前为止所做的:

ExampleApplication.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()

    }
}

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

SampleActivity.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 ?

    }
}

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

6 个答案:

答案 0 :(得分:18)

关于泄漏金丝雀的好处是它的自动化程度。 默认情况下,它已经&#34; watch&#34;对于未正确GCed的活动。因此,开箱即用,如果有任何活动泄露,您应该收到通知。

在我的项目中,我在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);
        }
    }
}

因此垃圾收集和内存泄漏以及金丝雀这些重要的事情就是要知道应该收集什么东西并要求查看该项目。

例如,我们使用&#34;基本片段&#34;使用以下代码:

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

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

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

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

我希望它有所帮助。

答案 1 :(得分:5)

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

只需添加以下依赖项即可。无需更多代码。

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

答案 2 :(得分:2)

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

如何使用LeakCanary

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

How to Use LeakCanary (4分13秒)

我必须克服的问题之一是,我必须以常规运行模式而不是调试模式启动该应用程序。我还必须偏离基本说明,并按如下所示设置应用程序级别的build.gradle文件:

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

我认为debugImplementation对我不起作用,因为LeakCanary ignores leak detection when debugging。尽管LeakCanary表示它提供了"no-op" version of its library for release,但是由于调试对我不起作用,我将上面的releaseImplementation从推荐的com.squareup.leakcanary:leakcanary-android-no-op:1.5.4更改为com.squareup.leakcanary:leakcanary-android:1.5.4

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

我也注意到了Sometimes it could take up to a few minutes for the analysis to complete and show a memory leak notification on your phone

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

现在,我已经修复了一些内存泄漏,我使用LeakCanary来验证此修复程序。但是,如果LeakCanary不报告任何内容,则我不一定认为那是因为我的漏洞已修复。可能只是LeakCanary损坏了。

How to verify a memory leak fix using LeakCanary (16分34秒)

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

由于LeakCanary在工作时仅显示很少的状态信息,因此很难知道它是否在做任何事情。这使我认为我已经修复了内存泄漏,而实际上却没有。以上三个步骤是我发现使用LeakCanary验证内存泄漏修复程序的最佳方法。

答案 3 :(得分:1)

我在应用程序中使用

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);

}

}

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

答案 4 :(得分:1)

要使用 LeakCanary,请将leakcanary-android 依赖项添加到您的应用程序

build.gradle 文件:

dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
}

就是这样,不需要更改代码!

通过过滤 Logcat 中的 LeakCanary 标签来确认 LeakCanary 在启动时正在运行:

D LeakCanary: LeakCanary is running and ready to detect leaks
Info

LeakCanary 自动检测以下对象的泄漏:

destroyed Activity instances
destroyed Fragment instances
destroyed fragment View instances
cleared ViewModel instances

enter image description here https://square.github.io/leakcanary/

答案 5 :(得分:0)

我使用了如下的Leak-Canary:

1)Gradle依赖项:

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

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类:将此类放在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)注册清单文件服务和1个权限:

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

5)最后验证设置是否成功:泄漏活动;)

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}, 
}
}

杀死该活动并检查带有标签:LeakCanary的日志

应该可以...