LeakCanary 是由 Square 開源的針對 Android
和 Java
的內存泄漏檢測工具。java
LeakCanary
的集成過程很簡單,首先在 build.gradle
文件中添加依賴:android
dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' }
debug
和 release
版本中使用的是不一樣的庫。LeakCanary
運行時會常常執行 GC
操做,在 release
版本中會影響效率。android-no-op
版本中基本沒有邏輯實現,用於 release
版本。git
而後實現本身的 Application
類:github
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... } }
這樣就集成完成了。當 LeakCanary
檢測到內存泄露時,會自動彈出 Notification
通知開發者發生內存泄漏的 Activity
和引用鏈,以便進行修復。服務器
從入口函數 LeakCanary.install(this)
開始分析:併發
LeakCanary.java
app
/** * 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.java
dom
/** Builder to create a customized {@link RefWatcher} with appropriate Android defaults. */ public static AndroidRefWatcherBuilder refWatcher(Context context) { return new AndroidRefWatcherBuilder(context); }
refWatcher()
方法新建了一個 AndroidRefWatcherBuilder
對象,該對象繼承於 RefWatcherBuilder
類,配置了一些默認參數,利用建造者構建一個 RefWatcher
對象。ide
AndroidRefWatcherBuilder.java
函數
public AndroidRefWatcherBuilder listenerServiceClass( Class<? extends AbstractAnalysisResultService> listenerServiceClass) { return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass)); }
RefWatcherBuilder.java
/** @see HeapDump.Listener */ public final T heapDumpListener(HeapDump.Listener heapDumpListener) { this.heapDumpListener = heapDumpListener; return self(); }
DisplayLeakService.java
/** * 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 {}
listenerServiceClass()
方法綁定了一個後臺服務 DisplayLeakService
,這個服務主要用來分析內存泄漏結果併發送通知。你能夠繼承並重寫這個類來進行一些自定義操做,好比上傳分析結果等。
RefWatcherBuilder.java
public final T excludedRefs(ExcludedRefs excludedRefs) { this.excludedRefs = excludedRefs; return self(); }
AndroidExcludedRefs.java
/** * 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; }
excludedRefs()
方法定義了一些對於開發者能夠忽略的路徑,意思就是即便這裏發生了內存泄漏,LeakCanary
也不會彈出通知。這大可能是系統 Bug 致使的,無需用戶進行處理。
最後調用 buildAndInstall()
方法構建 RefWatcher
實例並開始監聽 Activity
的引用:
AndroidRefWatcherBuilder.java
/** * Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+). */ public RefWatcher buildAndInstall() { RefWatcher refWatcher = build(); if (refWatcher != DISABLED) { LeakCanary.enableDisplayLeakActivity(context); ActivityRefWatcher.install((Application) context, refWatcher); } return refWatcher; }
看一下主要的 build()
和 install()
方法:
RefWatcherBuilder.java
/** Creates a {@link RefWatcher}. */ public final RefWatcher build() { if (isDisabled()) { return RefWatcher.DISABLED; } ExcludedRefs excludedRefs = this.excludedRefs; if (excludedRefs == null) { excludedRefs = defaultExcludedRefs(); } HeapDump.Listener heapDumpListener = this.heapDumpListener; if (heapDumpListener == null) { heapDumpListener = defaultHeapDumpListener(); } DebuggerControl debuggerControl = this.debuggerControl; if (debuggerControl == null) { debuggerControl = defaultDebuggerControl(); } HeapDumper heapDumper = this.heapDumper; if (heapDumper == null) { heapDumper = defaultHeapDumper(); } WatchExecutor watchExecutor = this.watchExecutor; if (watchExecutor == null) { watchExecutor = defaultWatchExecutor(); } GcTrigger gcTrigger = this.gcTrigger; if (gcTrigger == null) { gcTrigger = defaultGcTrigger(); } return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener, excludedRefs); }
build()
方法利用建造者模式構建 RefWatcher
實例,看一下其中的主要參數:
watchExecutor
: 線程控制器,在 onDestroy()
以後而且主線程空閒時執行內存泄漏檢測debuggerControl
: 判斷是否處於調試模式,調試模式中不會進行內存泄漏檢測gcTrigger
: 用於 GC
,watchExecutor
首次檢測到可能的內存泄漏,會主動進行 GC
,GC
以後會再檢測一次,仍然泄漏的斷定爲內存泄漏,進行後續操做heapDumper
: dump
內存泄漏處的 heap
信息,寫入 hprof
文件heapDumpListener
: 解析完 hprof
文件並通知 DisplayLeakService
彈出提醒excludedRefs
: 排除能夠忽略的泄漏路徑接下來就是最核心的 install()
方法,這裏就開始觀察 Activity
的引用了。在這以前還執行了一步操做,LeakCanary.enableDisplayLeakActivity(context);
:
public static void enableDisplayLeakActivity(Context context) { setEnabled(context, DisplayLeakActivity.class, true); }
最後執行到 LeakCanaryInternals#setEnabledBlocking
:
public static void setEnabledBlocking(Context appContext, Class<?> componentClass, boolean enabled) { ComponentName component = new ComponentName(appContext, componentClass); PackageManager packageManager = appContext.getPackageManager(); int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED; // Blocks on IPC. packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP); }
這裏啓用了 DisplayLeakActivity
而且顯示應用圖標。注意,這是指的不是你本身的應用圖標,是一個單獨的 LeakCanary
的應用,用於展現內存泄露歷史的,入口函數是 DisplayLeakActivity
,在 AndroidManifest.xml 中能夠看到默認狀況下 android:enabled="false"
:
<activity android:theme="@style/leak_canary_LeakCanary.Base" android:name=".internal.DisplayLeakActivity" android:process=":leakcanary" android:enabled="false" android:label="@string/leak_canary_display_activity_label" android:icon="@mipmap/leak_canary_icon" android:taskAffinity="com.squareup.leakcanary.${applicationId}" > <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>
ActivityRefWatcher.java
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); }
watchActivities()
方法中先解綁生命週期回調註冊 lifecycleCallbacks
,再從新綁定,避免重複綁定。lifecycleCallbacks
監聽了 Activity
的各個生命週期,在 onDestroy()
中開始檢測當前 Activity
的引用。
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new Application.ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { } @Override public void onActivityResumed(Activity activity) { } @Override public void onActivityPaused(Activity activity) { } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { ActivityRefWatcher.this.onActivityDestroyed(activity); } }; void onActivityDestroyed(Activity activity) { refWatcher.watch(activity); }
下面着重分析 RefWatcher
是如何檢測 Activity
的。
調用 RefWatcher#watch
檢測 Activity
。 RefWatcher.java
/** * 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); ensureGoneAsync(watchStartNanoTime, reference); }
watch()
方法的參數是 Object
,LeakCanary
並不只僅是針對 Android
的,它能夠檢測任何對象的內存泄漏,原理都是一致的。
這裏出現了幾個新面孔,先來了解一下各自是什麼:
retainedKeys
: 一個 Set<String>
集合,每一個檢測的對象都對應着一個惟一的 key
,存儲在 retainedKeys
中KeyedWeakReference
: 自定義的弱引用,持有檢測對象和對用的 key
值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"); } }
queue
: ReferenceQueue
對象,和 KeyedWeakReference
配合使用這裏有個小知識點,弱引用和引用隊列 ReferenceQueue
聯合使用時,若是弱引用持有的對象被垃圾回收,Java 虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。即 KeyedWeakReference
持有的 Activity
對象若是被垃圾回收,該對象就會加入到引用隊列 queue
中。
接着看看具體的內存泄漏判斷過程:
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) { watchExecutor.execute(new Retryable() { @Override public Retryable.Result run() { return ensureGone(reference, watchStartNanoTime); } }); }
經過 watchExecutor
執行檢測操做,這裏的 watchExecutor
是 AndroidWatchExecutor
對象。
@Override protected WatchExecutor defaultWatchExecutor() { return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS); }
DEFAULT_WATCH_DELAY_MILLIS
爲 5 s。
public AndroidWatchExecutor(long initialDelayMillis) { mainHandler = new Handler(Looper.getMainLooper()); HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME); handlerThread.start(); backgroundHandler = new Handler(handlerThread.getLooper()); this.initialDelayMillis = initialDelayMillis; maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis; }
看看其中用到的幾個對象:
mainHandler
: 主線程消息隊列handlerThread
: 後臺線程,HandlerThread
對象,線程名爲 LeakCanary-Heap-Dump
backgroundHandler
: 上面的後臺線程的消息隊列initialDelayMillis
: 5 s,即以前的 DEFAULT_WATCH_DELAY_MILLIS
@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; } }); }
在具體的 execute()
過程當中,不論是 waitForIdle
仍是 postWaitForIdle
,最終仍是要切換到主線程中執行。要注意的是,這裏的 IdleHandler
究竟是何時去執行?
咱們都知道 Handler
是循環處理 MessageQueue
中的消息的,當消息隊列中沒有更多消息須要處理的時候,且聲明瞭 IdleHandler
接口,這是就會去處理這裏的操做。即指定一些操做,當線程空閒的時候來處理。當主線程空閒時,就會通知後臺線程延時 5 秒執行內存泄漏檢測工做。
void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) { long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor); long delayMillis = initialDelayMillis * exponentialBackoffFactor; backgroundHandler.postDelayed(new Runnable() { @Override public void run() { Retryable.Result result = retryable.run(); if (result == RETRY) { postWaitForIdle(retryable, failedAttempts + 1); } } }, delayMillis); }
下面是真正的檢測過程,AndroidWatchExecutor
在執行時調用 ensureGone()
方法:
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; }
再重複一次幾個變量的含義,retainedKeys
是一個 Set
集合,存儲檢測對象對應的惟一 key
值,queue
是一個引用隊列,存儲被垃圾回收的對象。
主要過程有一下幾步:
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); } }
遍歷引用隊列 queue
,判斷隊列中是否存在當前 Activity
的弱引用,存在則刪除 retainedKeys
中對應的引用的 key
值。
private boolean gone(KeyedWeakReference reference) { return !retainedKeys.contains(reference.key); }
判斷 retainedKeys
中是否包含當前 Activity
引用的 key
值。
若是不包含,說明上一步操做中 retainedKeys
移除了該引用的 key
值,也就說上一步操做以前引用隊列 queue
中包含該引用,GC
處理了該引用,未發生內存泄漏,返回 DONE
,再也不往下執行。
若是包含,並不會當即斷定發生內存泄漏,可能存在某個對象已經不可達,可是還沒有進入引用隊列 queue
。這時會主動執行一次 GC
操做以後再次進行判斷。
/** * 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(); } } }; void runGc(); }
注意這裏調用 GC
的寫法,並非使用 System.gc
。System.gc
僅僅只是通知系統在合適的時間進行一次垃圾回收操做,實際上並不能保證必定執行。
主動進行 GC
以後會再次進行斷定,過程同上。首先調用 removeWeaklyReachableReferences()
清除 retainedKeys
中弱引用的 key
值,再判斷是否移除。若是仍然沒有移除,斷定爲內存泄漏。
斷定內存泄漏以後,調用 heapDumper.dumpHeap()
進行處理:
AndroidHeapDumper.java
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null. @Override public File dumpHeap() { File heapDumpFile = leakDirectoryProvider.newHeapDumpFile(); if (heapDumpFile == RETRY_LATER) { return RETRY_LATER; } FutureResult<Toast> waitingForToast = new FutureResult<>(); showToast(waitingForToast); if (!waitingForToast.wait(5, SECONDS)) { CanaryLog.d("Did not dump heap, too much time waiting for Toast."); return RETRY_LATER; } Toast toast = waitingForToast.get(); try { Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); cancelToast(toast); return heapDumpFile; } catch (Exception e) { CanaryLog.d(e, "Could not dump heap"); // Abort heap dump return RETRY_LATER; } }
leakDirectoryProvider.newHeapDumpFile()
新建了 hprof
文件,而後調用 Debug.dumpHprofData()
方法 dump
當前堆內存並寫入剛纔建立的文件。
回到 RefWatcher.ensureGone()
方法中,生成 heapDumpFile
文件以後,經過 heapdumpListener
分析。
heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs));
這裏的 heapdumpListener
是 ServiceHeapDumpListener
對象,接着進入 ServiceHeapDumpListener.runAnalysis()
方法。
@Override public void analyze(HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); }
這裏的 listenerServiceClass
指的是 DisplayLeakService.class
,文章開頭提到的 AndroidRefWatcherBuilder
中進行了配置。
@Override protected HeapDump.Listener defaultHeapDumpListener() { return new ServiceHeapDumpListener(context, DisplayLeakService.class); }
HeapAnalyzerService.runAnalysis()
方法中啓動了它本身,傳遞了兩個參數,DisplayLeakService
類名和要分析的 heapDump
。啓動本身後,在 onHandleIntent
中進行處理。
/** * 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"; public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) { Intent intent = new Intent(context, HeapAnalyzerService.class); intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName()); intent.putExtra(HEAPDUMP_EXTRA, heapDump); context.startService(intent); } public HeapAnalyzerService() { super(HeapAnalyzerService.class.getSimpleName()); } @Override protected void onHandleIntent(Intent intent) { if (intent == null) { CanaryLog.d("HeapAnalyzerService received a null intent, ignoring."); return; } String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA); HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA); HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs); AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey); AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result); } }
checkForLeak
方法中主要使用了 Square
公司的另外一個庫 haha 來分析 Android heap dump
,獲得結果後回調給 DisplayLeakService
。
public static void sendResultToListener(Context context, String listenerServiceClassName, HeapDump heapDump, AnalysisResult result) { Class<?> listenerServiceClass; try { listenerServiceClass = Class.forName(listenerServiceClassName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } Intent intent = new Intent(context, listenerServiceClass); intent.putExtra(HEAP_DUMP_EXTRA, heapDump); intent.putExtra(RESULT_EXTRA, result); context.startService(intent); }
一樣在 onHandleIntent
中進行處理。
@Override protected final void onHandleIntent(Intent intent) { HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA); AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA); try { onHeapAnalyzed(heapDump, result); } finally { //noinspection ResultOfMethodCallIgnored heapDump.heapDumpFile.delete(); } }
調用 onHeapAnalyzed()
以後,會將 hprof
文件刪除。
DisplayLeakService.java
@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { String leakInfo = leakInfo(this, heapDump, result, true); CanaryLog.d("%s", leakInfo); boolean resultSaved = false; boolean shouldSaveResult = result.leakFound || result.failure != null; if (shouldSaveResult) { heapDump = renameHeapdump(heapDump); resultSaved = saveResult(heapDump, result); } PendingIntent pendingIntent; String contentTitle; String contentText; if (!shouldSaveResult) { contentTitle = getString(R.string.leak_canary_no_leak_title); contentText = getString(R.string.leak_canary_no_leak_text); pendingIntent = null; } else if (resultSaved) { pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey); if (result.failure == null) { String size = formatShortFileSize(this, result.retainedHeapSize); String className = classSimpleName(result.className); if (result.excludedLeak) { contentTitle = getString(R.string.leak_canary_leak_excluded, className, size); } else { contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size); } } else { contentTitle = getString(R.string.leak_canary_analysis_failed); } contentText = getString(R.string.leak_canary_notification_message); } else { contentTitle = getString(R.string.leak_canary_could_not_save_title); contentText = getString(R.string.leak_canary_could_not_save_text); pendingIntent = null; } // New notification id every second. int notificationId = (int) (SystemClock.uptimeMillis() / 1000); showNotification(this, contentTitle, contentText, pendingIntent, notificationId); afterDefaultHandling(heapDump, result, leakInfo); }
根據分析結果,調用 showNotification()
方法構建了一個 Notification
向開發者通知內存泄漏。
public static void showNotification(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent pendingIntent, int notificationId) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); Notification notification; Notification.Builder builder = new Notification.Builder(context) // .setSmallIcon(R.drawable.leak_canary_notification) .setWhen(System.currentTimeMillis()) .setContentTitle(contentTitle) .setContentText(contentText) .setAutoCancel(true) .setContentIntent(pendingIntent); if (SDK_INT >= O) { String channelName = context.getString(R.string.leak_canary_notification_channel); setupNotificationChannel(channelName, notificationManager, builder); } if (SDK_INT < JELLY_BEAN) { notification = builder.getNotification(); } else { notification = builder.build(); } notificationManager.notify(notificationId, notification); }
最後還會執行一個空實現的方法 afterDefaultHandling
:
/** * 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) { }
你能夠重寫這個方法進行一些自定義的操做,好比向服務器上傳泄漏的堆棧信息等。
這樣,LeakCanary
就完成了整個內存泄漏檢測的過程。能夠看到,LeakCanary
的設計思路十分巧妙,同時也很清晰,有不少有意思的知識點,像對於弱引用和 ReferenceQueue
的使用, IdleHandler
的使用,四大組件的開啓和關閉等等,都很值的你們去深究。