问题
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 :
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()
}
}
Let's say I have an Activity where I want LeakCanary to watch an object
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 ?
}
}
Now how do I use LeakCanary to see which object is causing the memory leak. Thank you for your help and assistance.
回答1:
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.
On my project I've added an extra method on the Application
like this:
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 LeakCanary
is trying to check if any fragment is leaking memory.
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);
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.
回答2:
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.
How to Use LeakCanary
Here is a basic example of how to LeakCanary working:
How to Use LeakCanary (4 minutes 13 seconds)
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.gradle
file as follows:
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
}
I think debugImplementation
didn'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 releaseImplementation
above from the recommended com.squareup.leakcanary:leakcanary-android-no-op:1.5.4
to com.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.
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
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.
How to verify a memory leak fix using LeakCanary (16 minutes 34 seconds)
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
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.
回答3:
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
回答4:
I used Leak-Canary like below:
1) Gradle dependency:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.4'
2) Application class:
@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.
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:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<service android:name=".LeakLoggerService" />
5) Finally verify if setup is successful: 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
It should work...
来源:https://stackoverflow.com/questions/33654503/how-to-use-leak-canary