<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] # LeakCanary源碼分析 > 基于版本 1.6.3 ## 使用 ### 依賴 ```java dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3' // Optional, if you use support library fragments: debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3' } ``` ### 初始化 Application 注入 初始化代碼 ```java public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); //LeakCanary.isInAnalyzerProcess(this)會調用 sInServiceProcess(context, HeapAnalyzerService.class)來判斷是否跟HeapAnalyzerService在同一個進程中。 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... } } ``` aar 自帶 以下不需要開發者編寫 HeapAnalyzerService是一個IntentServcie,主要用來分析生成的hprof文件。我們看下HeapAnalyzerService的清單配置: ```xml <service android:name=".internal.HeapAnalyzerService" android:process=":leakcanary" android:enabled="false" /> ``` 可以看到,這個服務運行在單獨的“:leakcanary”進程中。如果你瀏覽下LeakCanary的其他android組件(DisplayLeakService,DisplayLeakActivity等)清單配置,會發現它們都是運行在這個進程中的。為什么HeapAnalyzerService要單獨放在這個進程?因為內存泄露時,生成的hprof文件很大,解析的時候會耗費大量內存,如果放在app主進程中,可能會導致OOM。 另外,我們也注意到`android:enabled="false"`這個屬性設置,那說明默認情況下,HeapAnalyzerService這個service是不可用的,**那它什么時候打開呢?** 帶著這個疑問我們接著往下看: ```java //com.squareup.leakcanary.internal.LeakCanaryInternals public static boolean isInServiceProcess(Context context, Class<? extends Service> serviceClass) { PackageManager packageManager = context.getPackageManager(); PackageInfo packageInfo; try { packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES); } catch (Exception e) { CanaryLog.d(e, "Could not get package info for %s", context.getPackageName()); return false; } String mainProcess = packageInfo.applicationInfo.processName; ComponentName component = new ComponentName(context, serviceClass); ServiceInfo serviceInfo; try { serviceInfo = packageManager.getServiceInfo(component, 0); } catch (PackageManager.NameNotFoundException ignored) { // Service is disabled. /****標記1*******.首次運行的時候,HeapAnalyzerService組件沒有打開,所以到這里就返回了***/ return false; } //HeapAnalyzerService激活后才會有下邊的代碼 if (serviceInfo.processName.equals(mainProcess)) {/****標記2*******/ CanaryLog.d("Did not expect service %s to run in main process %s", serviceClass, mainProcess); // Technically we are in the service process, but we're not in the service dedicated process. return false; } int myPid = android.os.Process.myPid(); ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.RunningAppProcessInfo myProcess = null; List<ActivityManager.RunningAppProcessInfo> runningProcesses; try { runningProcesses = activityManager.getRunningAppProcesses(); } catch (SecurityException exception) { // https://github.com/square/leakcanary/issues/948 CanaryLog.d("Could not get running app processes %d", exception); return false; } if (runningProcesses != null) { for (ActivityManager.RunningAppProcessInfo process : runningProcesses) { if (process.pid == myPid) { myProcess = process; break; } } } if (myProcess == null) { CanaryLog.d("Could not find running process for %d", myPid); return false; } return myProcess.processName.equals(serviceInfo.processName); } ``` 上邊我們說過,HeapAnalyzerService組件默認是關閉的,所以一次執行的時候,到代碼中1標識的位置就結束了。第二次執行的時候,才會往下走。此外,在2標記的的位置,如果你把HeapAnalyzerService的進程設置為跟主進程一樣,LeakCanary依然可以工作,但要小心OOM。再往下就是判斷HeapAnalyzerService服務進程是否跟主進程一樣。 ### 查看內存泄漏 ## 原理 ### 如何判斷內存泄漏 > 弱引用探測內存泄露 WeakReference(T referent, ReferenceQueue<? super T> q) referent被gc回收時,會將包裹它的弱引用注冊到ReferenceQueue中,在gc后判斷ReferenceQueue有沒有referent包裹的WeakReference,就可以判斷是否被gc正常回收。 ### `LeakCanary.install` ```java public static @NonNull RefWatcher install(@NonNull Application application) { return refWatcher(application).listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) .buildAndInstall(); } public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) { return new AndroidRefWatcherBuilder(context); } ``` intall方法中,生成了AndroidRefWatcherBuilder對象,設置了一些參數,并最終調用了buildAndInstall()。 ### `AndroidRefWatcherBuilder` |參數類型|默認實現|android平臺實現類|含義| | --- | --- | ---| --- | |HeapDump.Listener | HeapDump.Listener.NONE |ServiceHeapDumpListener |執行分析HeapDump,在android中是在啟動DisplayLeakService在一個新的的進程中,執行解析dump | |DebuggerControl |DebuggerControl.NONE |AndroidDebuggerControl |判斷進程是否處于debug狀態 | |HeapDumper |HeapDumper.NONE |AndroidHeapDumper |dump heap到文件中 | |GcTrigger |GcTrigger.DEFAULT |GcTrigger.DEFAULT |主動調用GC| |WatchExecutor |WatchExecutor.NONE |AndroidWatchExecutor |延遲調用GC的執行器;在android平臺是內部有一個HandThread,通過 handler來做延遲操作| |HeapDump.Builder |HeapDump.Builder |HeapDump.Builder |主要是構造執行dump時的一些參數| 注意兩點: + AndroidRefWatcherBuilder與RefWatcherBuilder使用了builder模式,可以看下是怎么用泛型實現Builder模式的繼承結構的。 + 參數里邊的默認實現,都是在接口定義中使用匿名內部類定義了一個默認的的實現,這種寫法可以參考,比如DebuggerControl的定義 ```java public interface DebuggerControl { DebuggerControl NONE = new DebuggerControl() { @Override public boolean isDebuggerAttached() { return false; } }; boolean isDebuggerAttached(); } ``` 上邊說的HeapDump.Builder(Builder模式),最終會構造一個HeapDump對象,看下他有哪些參數: |參數名稱 |類型 |含義 | | --- | --- | --- | |heapDumpFile |File |hprof文件路徑 | |referenceKey |String |是一個UUID,用來唯一標識一個要監測的對象的| |referenceName |String |用戶給監控對象自己定義的名字| |excludedRefs |ExcludedRefs |要排除的類集合;因為有些內存泄露,不|是我們的程序導致的,而是系統的bug,這些bug我們無能為力,所以做了這樣一個列|表,把這些泄露問題排除掉,不會展示給我們| |watchDurationMs |long |執行RefWatcher.watch()之后,到最終監測到內|存泄露花費的時間| |gcDurationMs |long |手動執行gc花費的時間| |heapDumpDurationMs |long |記錄dump內存花費的時間| |computeRetainedHeapSize |boolean |是否計算持有的堆的大小| |reachabilityInspectorClasses |List<Class<? extends Reachability.Inspector>> | ? | + AndroidRefWatcherBuilder#listenerServiceClass() ```java /** * Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This * overrides any call to {@link #heapDumpListener(HeapDump.Listener)}. */ public @NonNull AndroidRefWatcherBuilder listenerServiceClass( @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) { enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass); return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass)); } ``` `ServiceHeapDumpListener`:先簡單說下這個listsener的作用,當監測到內存泄露時,就會hprof文件回調給這個listener,在回調中它會啟啟動獨立的進程,解析這這個文件,并將解析結果傳給`AbstractAnalysisResultService`的實現類,也即install方法中傳入的`DisplayLeakService`,`DisplayLeakService`會彈出系統通知提示發生了內存泄露,這個listener將整個流程串了起來。 `DisplayLeakService` 最后會講到! 再往下看最后調用的`AndroidRefWatcherBuilder.buildAndInstall()`: ```java /** * Creates a {@link RefWatcher} instance and makes it available through {@link * LeakCanary#installedRefWatcher()}. * * Also starts watching activity references if {@link #watchActivities(boolean)} was set to true. * * @throws UnsupportedOperationException if called more than once per Android process. */ public @NonNull RefWatcher buildAndInstall() { if (LeakCanaryInternals.installedRefWatcher != null) { throw new UnsupportedOperationException("buildAndInstall() should only be called once."); } RefWatcher refWatcher = build(); if (refWatcher != DISABLED) { if (enableDisplayLeakActivity) { LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true); } if (watchActivities) { ActivityRefWatcher.install(context, refWatcher); } if (watchFragments) { FragmentRefWatcher.Helper.install(context, refWatcher); } } LeakCanaryInternals.installedRefWatcher = refWatcher; return refWatcher; } ``` build()是用之前傳的參數構造了一個RefWatcher對象返回給我們,檢測對象內存泄露的邏輯都是這個類處理的。 接下來異步激活組件DisplayLeakActivity,它是用來展示泄露列表信息的,激活組件調用的是 **packageManager.setComponentEnabledSetting()** 這個api,它可以實現一些特殊的功能,比如如果有系統權限,可以屏蔽應用開機監聽廣播,有興趣的可以google一下。因為這個方法可能會阻塞IPC通信,所以放在了異步里邊執行。 + LeakCanaryInternals.setEnabledAsync() ```java public static void setEnabledAsync(Context context, final Class<?> componentClass, final boolean enabled) { final Context appContext = context.getApplicationContext(); AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { @Override public void run() { setEnabledBlocking(appContext, componentClass, enabled); } }); } 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); } ``` 激活之后,我們桌面上會多一個圖標,為什么會多一個圖標?謎底就在他的清單配置中的intent-filter ```xml 激活之后,我們桌面上會多一個圖標,為什么會多一個圖標?謎底就在他的清單配置中的intent-filter <activity android:theme="@style/leak_canary_LeakCanary.Base" android:name=".internal.DisplayLeakActivity" android:process=":leakcanary" .... > <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> ``` 那具體什么時候觸發檢測的呢?這就是ActivityRefWatcher和FragmentRefWatcher要做的事兒。對于Activity和Fragment來說,當它的onDestory()被回調之后,就應該被系統回調掉。 ### `ActivityRefWatcher` + com.squareup.leakcanary.ActivityRefWatcher#install() ```java public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) { Application application = (Application) context.getApplicationContext(); ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher); application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks); } private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityDestroyed(Activity activity) { refWatcher.watch(activity); } }; ``` 到這里我們就能證實了當`Acitivity.onDestroy()` 監聽內存泄露 `refWatcher.watch(activity);` ### `FragmentRefWatcher` + com.squareup.leakcanary.internal.FragmentRefWatcher.Helper#install() ```java public static void install(Context context, RefWatcher refWatcher) { List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>(); if (SDK_INT >= O) { fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher)); } try { Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME); Constructor<?> constructor = fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class); FragmentRefWatcher supportFragmentRefWatcher = (FragmentRefWatcher) constructor.newInstance(refWatcher); fragmentRefWatchers.add(supportFragmentRefWatcher); } catch (Exception ignored) { } if (fragmentRefWatchers.size() == 0) { return; } Helper helper = new Helper(fragmentRefWatchers); Application application = (Application) context.getApplicationContext(); application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks); } private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { for (FragmentRefWatcher watcher : fragmentRefWatchers) { watcher.watchFragments(activity); } } }; ``` Fragment的onDestory() `監聽的是Acitivity#onDestory()` 監聽要麻煩一些,我們知道以前不管是原生和support庫中的Fragment是沒有統一的系統回調的,后來在Support庫中和android O以上的版本增加了對fragment的生命周期回調`fragmentManager.registerFragmentLifecycleCallbacks()`。 我們只看下是如何對Support庫中的fragment處理的。 + com.squareup.leakcanary.internal.AndroidOFragmentRefWatcher ```java @RequiresApi(Build.VERSION_CODES.O) // class AndroidOFragmentRefWatcher implements FragmentRefWatcher { private final RefWatcher refWatcher; AndroidOFragmentRefWatcher(RefWatcher refWatcher) { this.refWatcher = refWatcher; } private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = new FragmentManager.FragmentLifecycleCallbacks() { @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) { View view = fragment.getView(); if (view != null) { refWatcher.watch(view); } } @Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) { refWatcher.watch(fragment); } }; @Override public void watchFragments(Activity activity) { FragmentManager fragmentManager = activity.getFragmentManager(); fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true); } } ``` + com.squareup.leakcanary.internal.SupportFragmentRefWatcher ```java class SupportFragmentRefWatcher implements FragmentRefWatcher { private final RefWatcher refWatcher; SupportFragmentRefWatcher(RefWatcher refWatcher) { this.refWatcher = refWatcher; } private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = new FragmentManager.FragmentLifecycleCallbacks() { @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) { View view = fragment.getView(); if (view != null) { refWatcher.watch(view); } } @Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) { refWatcher.watch(fragment); } }; @Override public void watchFragments(Activity activity) { if (activity instanceof FragmentActivity) { FragmentManager supportFragmentManager = ((FragmentActivity) activity).getSupportFragmentManager(); supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true); } } } ``` 可以看到,對于Fragment來對,LeakCanary除了監控fragment又沒有回收外,還會監測Fragment的view有沒有被及時回收。AndroidOFragmentRefWatcher的代碼跟上邊的類似。 在android O及以上版本是通過`AndroidOFragmentRefWatcher`來處理,如果用的是最新的support庫,是通過`SupportFragmentRefWatcher`來處理,他們都繼承在FragmentRefWatcher。`AndroidOFragmentRefWatcher`和 `SupportFragmentRefWatcher` 代碼基本一樣為了適配API ! ### `RefWatcher.watch()` >從上可以看到,不管是`Fragment`,`Activity`還是`View`最終都調用了同一個`RefWatcher.watch()`方法, 以Activity: ```java /** * 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); } private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) { // com.squareup.leakcanary.AndroidWatchExecutor watchExecutor watchExecutor.execute(new Retryable() { @Override public Retryable.Result run() { return ensureGone(reference, watchStartNanoTime); } }); } ``` `KeyedWeakReference` 繼承自WeakReference,增加的key和referenceName,這個key會同時放入Set集合中,方便gc回收對象后,做對比。 `watchExecutor` 在Android中實現是`AndroidWatchExecutor` ```java // com.squareup.leakcanary.AndroidWatchExecutor#execute @Override public void execute(@NonNull Retryable retryable) { if (Looper.getMainLooper().getThread() == Thread.currentThread()) { waitForIdle(retryable, 0); } else { postWaitForIdle(retryable, 0); } } ``` `waitForIdle` 和 `postWaitForIdle` 最終還是調用 `waitForIdle` ```java private 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; } }); } private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) { long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor); long delayMillis = initialDelayMillis * exponentialBackoffFactor; // HandlerThread backgroundHandler.postDelayed(new Runnable() { @Override public void run() { Retryable.Result result = retryable.run(); if (result == RETRY) { postWaitForIdle(retryable, failedAttempts + 1); } } }, delayMillis); } ``` 這個類會在主UI線程空閑的時候,向內部的HandlerThread發送一個消息,執行execute方法傳給他的Retryable對象, 調用`retryable.run()`,如果返回RETRY會繼續等待下一個主UI線程空閑回調,直至返回為Done,每次重試都會增大間隔時間。 **注意:是通過 Looper.myQueue().addIdleHandler()來監聽系統主線程空閑的** 到這里我們就應該知道`ensureGone`最開始這個方法被執行的 `HandlerThread`線程中 ```java @SuppressWarnings("ReferenceEquality") // Explicitly checking for named null. Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); //第一次調用如果系統GC 減少引用 removeWeaklyReachableReferences(); if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } if (gone(reference)) { return DONE; } gcTrigger.runGc(); //第二次調用 主動GC 在判斷引用 減少誤報 removeWeaklyReachableReferences(); if (!gone(reference)) { //到這里就有可能出現內存泄露了 long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); // 關鍵時刻 heapDumper 實現是 com.squareup.leakcanary.AndroidHeapDumper File heapDumpFile = heapDumper.dumpHeap(); //生成 dump 文件 if (heapDumpFile == RETRY_LATER) { // Could not dump the heap. return RETRY; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); // 將dump文件構造成HeapDump 對象 HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key) .referenceName(reference.name) .watchDurationMs(watchDurationMs) .gcDurationMs(gcDurationMs) .heapDumpDurationMs(heapDumpDurationMs) .build(); //HeapDump 對象 heapdumpListener對應的是ServiceHeapDumpListener的analyze() heapdumpListener.analyze(heapDump); } return DONE; } private boolean gone(KeyedWeakReference reference) { return !retainedKeys.contains(reference.key); } 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); } } ``` `removeWeaklyReachableReferences`調用了兩次,第一次調用后,如果系統gc正常回收,retainedKeys就不會再包含`KeyedWeakReference` 的key;如果沒有回收,會手動調用gc,再判斷,減少誤報;如果兩次gc后,對象仍然沒有被回收,可能就是真的內存泄露了。再調用heapDumper.dumpHeap()得到內存數據。heapDumper對應的是`AndroidHeapDumper`,`LeakDirectoryProvider` 指定了dumperHeap()的文件夾路徑。 `heapDumper.dumpHeap();`生成文件后,并且生成HeapDump 對象,HeapDump 對象 heapdumpListener對應的是`ServiceHeapDumpListener`的analyze() + com.squareup.leakcanary.AndroidHeapDumper ```java @Override protected @NonNull HeapDumper defaultHeapDumper() { LeakDirectoryProvider leakDirectoryProvider = LeakCanaryInternals.getLeakDirectoryProvider(context); return new AndroidHeapDumper(context, leakDirectoryProvider); } @Override @Nullable 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; } Notification.Builder builder = new Notification.Builder(context) .setContentTitle(context.getString(R.string.leak_canary_notification_dumping)); Notification notification = LeakCanaryInternals.buildNotification(context, builder); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); int notificationId = (int) SystemClock.uptimeMillis(); notificationManager.notify(notificationId, notification); Toast toast = waitingForToast.get(); try { Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); cancelToast(toast); notificationManager.cancel(notificationId); return heapDumpFile; } catch (Exception e) { CanaryLog.d(e, "Could not dump heap"); // Abort heap dump return RETRY_LATER; } } ``` dumpHeap會彈出一個通知,顯示Dumping,并調用`Debug.dumpHprofData(heapDumpFile.getAbsolutePath())`來保存內存,如果異常dumpHprofData異常會重試。 如果dump成功heapDumper.dumpHeap(),會構造HeapDump 對象,`ensureGone() --> heapdumpListener.analyze(heapDump);` 并傳遞給`heapdumpListener`。前邊我們說過`heapdumpListener`對應的是`ServiceHeapDumpListener`的analyze() > heapdumpListener 是在 `com.squareup.leakcanary.LeakCanary#install()`中調用 `listenerServiceClass`,`heapDumpListener()` ```java // com.squareup.leakcanary.ServiceHeapDumpListener#analyze @Override public void analyze(@NonNull HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); } // com.squareup.leakcanary.internal.HeapAnalyzerService#runAnalysis public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) { setEnabledBlocking(context, HeapAnalyzerService.class, true); setEnabledBlocking(context, listenerServiceClass, true); Intent intent = new Intent(context, HeapAnalyzerService.class); intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName()); intent.putExtra(HEAPDUMP_EXTRA, heapDump); ContextCompat.startForegroundService(context, intent); } ``` **[注意]**:`DisplayLeakActivity`,已經調用pm.setComponentEnabledSetting()方法激活組件了,但listenerServiceClass對應的`DisplayLeakService`和`HeapAnalyzerService`還沒有激活,所以在啟動`HeapAnalyzerService`時,要先把這兩個組件激活。 `HeapAnalyzerService`是一個前臺IntentService,最終回調到他的`onHandleIntentInForeground`方法: ```java @Override protected void onHandleIntentInForeground(@Nullable 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, this, heapDump.reachabilityInspectorClasses); //生成泄露對象引用鏈 AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey, heapDump.computeRetainedHeapSize); // listenerClassName=DisplayLeakService AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result); } ``` 在onHandleIntentInForeground中根據Intent的參數,構造了heapAnalyzer ,調用`heapAnalyzer.checkForLeak()`得到泄露對象的引用鏈。 ```java public static void sendResultToListener(@NonNull Context context, @NonNull String listenerServiceClassName, @NonNull HeapDump heapDump, @NonNull AnalysisResult result) { Class<?> listenerServiceClass; // DisplayLeakService try { listenerServiceClass = Class.forName(listenerServiceClassName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } Intent intent = new Intent(context, listenerServiceClass); File analyzedHeapFile = AnalyzedHeap.save(heapDump, result); if (analyzedHeapFile != null) { intent.putExtra(ANALYZED_HEAP_PATH_EXTRA, analyzedHeapFile.getAbsolutePath()); } ContextCompat.startForegroundService(context, intent); } ``` 返回的結果通過AnalyzedHeap.save(heapDump, result)保存到了一個文件沖,并返回給listenerServiceClassName代表的Sersvice,即DisplayLeakService,這個服務也是一個前臺IntentService,最終回調到DisplayLeakService的onHeapAnalyzed方法: ```java public class DisplayLeakService extends AbstractAnalysisResultService { @Override protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) { HeapDump heapDump = analyzedHeap.heapDump; AnalysisResult result = analyzedHeap.result; String leakInfo = leakInfo(this, heapDump, result, true); CanaryLog.d("%s", leakInfo); heapDump = renameHeapdump(heapDump); boolean resultSaved = saveResult(heapDump, result); String contentTitle; if (resultSaved) { PendingIntent pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey); if (result.failure != null) { contentTitle = getString(R.string.leak_canary_analysis_failed); } else { String className = classSimpleName(result.className); if (result.leakFound) { if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) { if (result.excludedLeak) { contentTitle = getString(R.string.leak_canary_leak_excluded, className); } else { contentTitle = getString(R.string.leak_canary_class_has_leaked, className); } } else { String size = formatShortFileSize(this, result.retainedHeapSize); if (result.excludedLeak) { contentTitle = getString(R.string.leak_canary_leak_excluded_retaining, className, size); } else { contentTitle = getString(R.string.leak_canary_class_has_leaked_retaining, className, size); } } } else { contentTitle = getString(R.string.leak_canary_class_no_leak, className); } } String contentText = getString(R.string.leak_canary_notification_message); showNotification(pendingIntent, contentTitle, contentText); } else { onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text)); } afterDefaultHandling(heapDump, result, leakInfo); } } ``` 將泄露結果在系統通知欄上顯示。 以上,LeakCanary從注冊Activity和Fragment的生命周期回調,到`onDestory`中開始`RefWatcher.watch()`,然后dumpHeap,再分析內存泄露引用鏈,最后將結果展示到系統通知欄的整個過程就基本分析完畢了。 ![](https://ws1.sinaimg.cn/large/882b6a2aly1g0hsp3z74ej219m0kfq5s.jpg) ## 總結 總結下看源碼過程中學到的知識點: + 內存泄露如何判斷(WeakReference) + Debug.dumpHprofData()獲取內存數據,Debug.isDebuggerConnected()判斷是否debug + setComponentEnabledSetting()動態開關組件 + Looper.myQueue().addIdleHandler()注冊UI線程空閑回調 [鏈接](https://www.jianshu.com/p/b857736d1461)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看