public class ExampleApplication extends Application {
@Override public void onCreate() { super.onCreate(); //判断是否在后台进程中 if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } LeakCanary.install(this); // Normal app init code... } }
if(serviceInfo.processName.equals(mainProcess)) { CanaryLog.d("Did not expect service %s to run in main process %s", new Object[]{serviceClass, mainProcess}); return false; } else { int myPid = Process.myPid(); ActivityManager activityManager = (ActivityManager)context.getSystemService("activity"); RunningAppProcessInfo myProcess = null; List runningProcesses = activityManager.getRunningAppProcesses(); if(runningProcesses != null) { Iterator var11 = runningProcesses.iterator();
/** * Creates a {@link RefWatcher} that works out of the box, and starts watching activity * references (on ICS+). */ public static RefWatcher install(Application application) { return refWatcher(application).listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) .buildAndInstall(); }
LeakCanary.refWatcher
LeakCanary.java
1 2 3 4
/** Builder to create a customized {@link RefWatcher} with appropriate Android defaults. */ public static AndroidRefWatcherBuilder refWatcher(Context context) { return new AndroidRefWatcherBuilder(context); }
/** @see HeapDump.Listener */ public final T heapDumpListener(HeapDump.Listener heapDumpListener) { this.heapDumpListener = heapDumpListener; return self(); }
DisplayLeakService.java
1 2 3 4 5 6 7 8
/** * Logs leak analysis results, and then shows a notification which will start {@link * DisplayLeakActivity}. * * You can extend this class and override {@link #afterDefaultHandling(HeapDump, AnalysisResult, * String)} to add custom behavior, e.g. uploading the heap dump. */ public class DisplayLeakService extends AbstractAnalysisResultService {}
/** * This returns the references in the leak path that can be ignored for app developers. This * doesn't mean there is no memory leak, to the contrary. However, some leaks are caused by bugs * in AOSP or manufacturer forks of AOSP. In such cases, there is very little we can do as app * developers except by resorting to serious hacks, so we remove the noise caused by those leaks. */ public static ExcludedRefs.Builder createAppDefaults() { return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class)); }
public static ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> refs) { ExcludedRefs.Builder excluded = ExcludedRefs.builder(); for (AndroidExcludedRefs ref : refs) { if (ref.applies) { ref.add(excluded); ((ExcludedRefs.BuilderWithParams) excluded).named(ref.name()); } } return excluded; }
public static void install(Application application, RefWatcher refWatcher) { new ActivityRefWatcher(application, refWatcher).watchActivities(); }
public void watchActivities() { // Make sure you don't get installed twice. stopWatchingActivities(); application.registerActivityLifecycleCallbacks(lifecycleCallbacks); }
/** * Identical to {@link #watch(Object, String)} with an empty string reference name. * * @see #watch(Object, String) */ public void watch(Object watchedReference) { watch(watchedReference, ""); }
/** * Watches the provided references and checks if it can be GCed. This method is non blocking, * the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed * with. * * @param referenceName An logical identifier for the watched object. */ public void watch(Object watchedReference, String referenceName) { if (this == DISABLED) { return; } checkNotNull(watchedReference, "watchedReference"); checkNotNull(referenceName, "referenceName"); final long watchStartNanoTime = System.nanoTime(); String key = UUID.randomUUID().toString(); retainedKeys.add(key); final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue);
@Override public void execute(Retryable retryable) { if (Looper.getMainLooper().getThread() == Thread.currentThread()) { waitForIdle(retryable, 0); } else { postWaitForIdle(retryable, 0); } }
void postWaitForIdle(final Retryable retryable, final int failedAttempts) { mainHandler.post(new Runnable() { @Override public void run() { waitForIdle(retryable, failedAttempts); } }); }
void waitForIdle(final Retryable retryable, final int failedAttempts) { // This needs to be called from the main thread. Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { postToBackgroundWithDelay(retryable, failedAttempts); return false; } }); }
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } if (gone(reference)) { return DONE; } gcTrigger.runGc(); removeWeaklyReachableReferences(); if (!gone(reference)) { long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile == RETRY_LATER) { // Could not dump the heap. return RETRY; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs)); } return DONE; }
private void removeWeaklyReachableReferences() { // WeakReferences are enqueued as soon as the object to which they point to becomes weakly // reachable. This is before finalization or garbage collection has actually happened. KeyedWeakReference ref; while ((ref = (KeyedWeakReference) queue.poll()) != null) { retainedKeys.remove(ref.key); } }
/** * Called when a watched reference is expected to be weakly reachable, but hasn't been enqueued * in the reference queue yet. This gives the application a hook to run the GC before the {@link * RefWatcher} checks the reference queue again, to avoid taking a heap dump if possible. */ public interface GcTrigger { GcTrigger DEFAULT = new GcTrigger() { @Override public void runGc() { // Code taken from AOSP FinalizationTest: // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/ // java/lang/ref/FinalizationTester.java // System.gc() does not garbage collect every time. Runtime.gc() is // more likely to perfom a gc. Runtime.getRuntime().gc(); enqueueReferences(); System.runFinalization(); }
private void enqueueReferences() { // Hack. We don't have a programmatic way to wait for the reference queue daemon to move // references to the appropriate queues. try { Thread.sleep(100); } catch (InterruptedException e) { throw new AssertionError(); } } };
/** * This service runs in a separate process to avoid slowing down the app process or making it run * out of memory. */ public final class HeapAnalyzerService extends IntentService {
private static final String LISTENER_CLASS_EXTRA = "listener_class_extra"; private static final String HEAPDUMP_EXTRA = "heapdump_extra";
/** * You can override this method and do a blocking call to a server to upload the leak trace and * the heap dump. Don't forget to check {@link AnalysisResult#leakFound} and {@link * AnalysisResult#excludedLeak} first. */ protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) { }