LeakCanary 源碼解析

LeakCanary 是什麼?

LeakCanary是Square公司基於MAT開源的一個工具,用來檢測Android App中的內存泄露問題。官方地址:https://github.com/square/leakcanary

工作原理分析:

1.怎麼檢測內存泄漏?什麼時候去檢測內存泄漏?

//註冊生命週期  在onDestory 的時候 觀察Activity 對象
 private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
        	//開始觀察
          refWatcher.watch(activity);
        }
      };



	// 此方法爲停止觀察activity  也就是不在監聽回掉
  public void stopWatchingActivities() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
  }

自己去跟源碼就會發現,LeakCanary 會 註冊一個ActivityLifecycleCallbacks ,當監聽到activity Ondestory 的時候,就會去觀察這個activity 是不是泄漏了,有沒有被GC回收。

具體怎麼觀察Activity 有沒有被回收?

  /**
   * 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);
  }


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

      HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
          .referenceName(reference.name)
          .watchDurationMs(watchDurationMs)
          .gcDurationMs(gcDurationMs)
          .heapDumpDurationMs(heapDumpDurationMs)
          .build();

      heapdumpListener.analyze(heapDump);
    }
    return DONE;
  }

我們看到,觀察activity 就是 把 activity 放到WeakRefence 裏面,WeakReference 的第二個參數是一個Qeue 對象,我們手動執行一下GC,然後Qeue.pull 一下,看看Qeue 裏面是不是有WeakRefence ,如果有說明對象被回收了。那麼這個activity 就沒有泄漏,否則就有可能泄露了導致回收不了。

這個機制是WeakReference 的機制。有興趣可以自行了解一下。

怎麼執行GC?

      Runtime.getRuntime().gc();
      enqueueReferences();
      System.runFinalization();

上面diam執行之後,我們再看下內存,還有沒有activity 的實例,如果還有,那麼說明這個activity 可能被泄露了。我們需要根據hprof 文件看下具體的調用鏈。

怎麼生成hprof?

如果activity 內存泄露了,我們需要hprof 文件去分析。那麼怎麼拿到hprof?

Debug.dumpHprofData(heapDumpFile.getAbsolutePath());

怎麼分析hprof

使用了下面的開源項目:

  implementation 'com.squareup.haha:haha:2.0.4'

分析完之後,用界面顯示出來就沒有什麼好說了。

怎麼在桌面有兩個圖標?

    <activity
        android:label="@string/app_name"
        android:name=".MainActivity"
        >
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
        <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
    </activity>

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

我們發現,leackCanary會生成一個自己的桌面圖標,點擊進去可以查看內漏泄露的信息,而且不需要創建桌面快捷方式的權限,就可以做到,他是用上面的方式做到的。清單裏面可以有多個"android.intent.category.LAUNCHER",當然也會有一個默認的。

怎麼設置組件啓用和關閉

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

也就這樣啦,沒有具體怎麼分析hprof,有興趣可以再深入研究了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章