美文网首页
LeakCanary源码分析

LeakCanary源码分析

作者: 风月寒 | 来源:发表于2021-01-09 15:53 被阅读0次
LeakCanary的使用

在LeakCanary2.0以上的只需要在dependencies里面加上下面的依赖即可。

debugImplementation (rootProject.ext.dependencies["leakcanary-android"]) {
        exclude group: 'com.android.support'
 }
LeakCanary的初始化

这时会有人说,除了添加依赖,没有写其他任何代码,他是怎么初始化的?首先了解一下application的创建过程,如果不了解可以看一下下面的文章。

https://www.jianshu.com/p/bd0496787dfd

在创建application的过程中,最终会调用handleBindApplication()。

private void handleBindApplication(AppBindData data) {
        ......
        Application app;
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
        final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
        try {
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                }
            }
            ......
            try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                      "Unable to create application " + app.getClass().getName()
                      + ": " + e.toString(), e);
                }
            }
        } 
    }

在handleBindApplication()方法中,显示调用makeApplication()创建一个application,它是通过反射的方式。application创建好了之后,调用mInstrumentation.callApplicationOnCreate(app);

public void callApplicationOnCreate(Application app) {
        app.onCreate();
}

callApplicationOnCreate主要是application回调onCreate方法。

但是在这过程中,有一段代码,

if (!data.restrictedBackupMode) {
    if (!ArrayUtils.isEmpty(data.providers)) {
        installContentProviders(app, data.providers);
    }
}

当data.providers不为空时,会初始化ContentProviders,这说明ContentProviders的onCreate会优先于Application的onCreate。

private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

在installContentProviders会调用installProvider方法。

private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        if (holder == null || holder.provider == null) {
            if (DEBUG_PROVIDER || noisy) {
                Slog.d(TAG, "Loading provider " + info.authority + ": "
                        + info.name);
            }
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            if (context.getPackageName().equals(ai.packageName)) {
                c = context;
            } else if (mInitialApplication != null &&
                    mInitialApplication.getPackageName().equals(ai.packageName)) {
                c = mInitialApplication;//1
            } else {
                try {
                    c = context.createPackageContext(ai.packageName,
                            Context.CONTEXT_INCLUDE_CODE);
                } catch (PackageManager.NameNotFoundException e) {
                    // Ignore
                }
            }
            if (c == null) {
                Slog.w(TAG, "Unable to get context for package " +
                      ai.packageName +
                      " while loading content provider " +
                      info.name);
                return null;
            }

            if (info.splitName != null) {
                try {
                    c = c.createContextForSplit(info.splitName);
                } catch (NameNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }

            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    // System startup case.
                    packageInfo = getSystemContext().mPackageInfo;
                }
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                if (DEBUG_PROVIDER) Slog.v(
                    TAG, "Instantiating local provider " + info.name);
                // XXX Need to create the correct context for this provider.
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                if (!mInstrumentation.onException(null, e)) {
                    throw new RuntimeException(
                            "Unable to get provider " + info.name
                            + ": " + e.toString(), e);
                }
                return null;
            }
        } else {
            provider = holder.provider;
            if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
                    + info.name);
        }

        ContentProviderHolder retHolder;

        synchronized (mProviderMap) {
            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                    + " / " + info.name);
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "installProvider: lost the race, "
                                + "using existing local provider");
                    }
                    provider = pr.mProvider;
                } else {
                    holder = new ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                if (prc != null) {
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "installProvider: lost the race, updating ref count");
                    }
                    // We need to transfer our new reference to the existing
                    // ref count, releasing the old one...  but only if
                    // release is needed (that is, it is not running in the
                    // system process).
                    if (!noReleaseNeeded) {
                        incProviderRefLocked(prc, stable);
                        try {
                            ActivityManager.getService().removeContentProvider(
                                    holder.connection, stable);
                        } catch (RemoteException e) {
                            //do nothing content provider object is dead any way
                        }
                    }
                } else {
                    ProviderClientRecord client = installProviderAuthoritiesLocked(
                            provider, localProvider, holder);
                    if (noReleaseNeeded) {
                        prc = new ProviderRefCount(holder, client, 1000, 1000);
                    } else {
                        prc = stable
                                ? new ProviderRefCount(holder, client, 1, 0)
                                : new ProviderRefCount(holder, client, 0, 1);
                    }
                    mProviderRefCountMap.put(jBinder, prc);
                }
                retHolder = prc.holder;
            }
        }
        return retHolder;
    }

在这个方法中,先是创建上下文,上下文的赋值分为三种,如果包名一致,直接使用传进来的context,否则,mInitialApplication不为空时,将mInitialApplication赋值给它,由前面可知mInitialApplication是在application创建后进行赋值初始化的。第三种就是调用createPackageContext()去创建。createPackageContext()是一个抽象方法,具体实现是在ContextWapper类中。

@Override
public Context createPackageContext(String packageName, int flags)
        throws PackageManager.NameNotFoundException {
    return mBase.createPackageContext(packageName, flags);
}

调用的是mBase的createPackageContext(),我们知道mBase是ContextImpl的实例对象,所以

@Override
    public Context createPackageContext(String packageName, int flags)
            throws NameNotFoundException {
        return createPackageContextAsUser(packageName, flags, mUser);
    }

    @Override
    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
            throws NameNotFoundException {
        if (packageName.equals("system") || packageName.equals("android")) {
            // The system resources are loaded in every application, so we can safely copy
            // the context without reloading Resources.
            return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
                    flags, null, null);
        }

        LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
        if (pi != null) {
            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
                    flags, null, null);

            final int displayId = getDisplayId();

            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
            if (c.mResources != null) {
                return c;
            }
        }

        // Should be a better exception.
        throw new PackageManager.NameNotFoundException(
                "Application package " + packageName + " not found");
    }

从上面可以知道,new 了一个ContexImp创建出来的。

创建好context之后,通过context.getClassLoader()得到ClassLoader。然后调用instantiateProvider()利用反射的方法创建出ContentProvider。

然后调用attachInfo()。

public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;
        mCallingPackage = new ThreadLocal<>();

        /*
         * Only allow it to be set once, so after the content service gives
         * this to us clients can't change it.
         */
        if (mContext == null) {
            mContext = context;
            if (context != null && mTransport != null) {
                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                        Context.APP_OPS_SERVICE);
            }
            mMyUid = Process.myUid();
            if (info != null) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                mExported = info.exported;
                mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
                setAuthorities(info.authority);
            }
            ContentProvider.this.onCreate();
        }
    }

在attachInfo会调用onCreate方法。LeakCanary就是采用这样的方法,

internal sealed class AppWatcherInstaller : ContentProvider()

继承ContentProvider,然后在AndroidManifest文件中声明。

<application>
    <provider
        android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
        android:authorities="${applicationId}.leakcanary-installer"
        android:exported="false" />
</application>

在打包的时候回进行Manifest的merge操作,然后我们安装的时候,就会解析AndroidManifest,进行启动的时候,就会执行这个段代码,,就这样LeakCanary进行初始化。

那是不是以后我们的在application的onCreate方法里面进行初始化的时候,都可以采用这种方法了?

答案是不可以的,因为都采用这种方法的话,一个应用的启动时间就会很慢,在实际操作中,我们需要对application的oncreate进行优化,一些不需要立马初始化的我们可以延时初始化,但是采用这种方法,我们就没法控制。

源码分析

从上面我们知道,会在AndroidManifest.xml中注册contentProvider,执行contentProvider的onCreate(),

override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    InternalAppWatcher.install(application)
    return true
  }

在onCreate方法中调用install()。

fun install(application: Application) {
    SharkLog.logger = DefaultCanaryLog()
    SharkLog.d { "Installing AppWatcher" }
    checkMainThread()
    if (this::application.isInitialized) {
      return
    }
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)//1
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)//2
    onAppWatcherInstalled(application)//3
  }

在install()中会初始化两个DestroyWatcher,分别为ActivityDestroyWatcher和FragmentDestroyWatcher,这两个的作用就是监视在Activity或Fragment在onDestory()对对象的回收。

ActivityDestroyWatcher.install
companion object {
    fun install(
      application: Application,
      objectWatcher: ObjectWatcher,
      configProvider: () -> Config
    ) {
      val activityDestroyWatcher =
        ActivityDestroyWatcher(objectWatcher, configProvider)
      application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
    }
  }
}

private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        if (configProvider().watchActivities) {
          objectWatcher.watch(
              activity, "${activity::class.java.name} received Activity#onDestroy() callback"
          )
        }
      }
    }

在install()中,先是创建一个ActivityDestroyWatcher对象,然后将自己的监听回调与application绑定。

在回调中,调用了watch(),

 @Synchronized fun watch(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      return
    }
    removeWeaklyReachableObjects()
    val key = UUID.randomUUID()
        .toString()
    val watchUptimeMillis = clock.uptimeMillis()
    val reference =
      KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
    SharkLog.d {
      "Watching " +
          (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
          (if (description.isNotEmpty()) " ($description)" else "") +
          " with key $key"
    }

    watchedObjects[key] = reference
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }

第一步,将可达activity删除,调用removeWeaklyReachableObjects()。

private fun removeWeaklyReachableObjects() {
    // 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.
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }

在GC或者finalization之前,在WeakReferences的被引用对象(这里是Activity)的可达性更改时,会把WeakReferences添加到创建时候指定的ReferenceQueue队列,这些可达性变更得对象,就是内存不泄露对象。

第二步:根据activity建对应的弱引用,并绑定ReferenceQueue。

第三步:将reference保存到watchedObjects数组中。

第四步:启动延时5s任务

在这是使用的是线程池,

private val checkRetainedExecutor = Executor {
    mainHandler.postDelayed(it, AppWatcher.config.watchDurationMillis)
  }
  val objectWatcher = ObjectWatcher(
      clock = clock,
      checkRetainedExecutor = checkRetainedExecutor,
      isEnabled = { AppWatcher.config.enabled }
  )

第五步:获取GC无法回收的Activity

@Synchronized private fun moveToRetained(key: String) {
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }

保存当前时间作为泄漏时间,然后回调通知InternalLeakCanary发生内存泄漏。

HeapDumpTrigger.class

override fun onObjectRetained() {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.onObjectRetained()
    }
  }
  
   fun onObjectRetained() {
    scheduleRetainedObjectCheck(
        reason = "found new object retained",
        rescheduling = false
    )
  }
  
private fun scheduleRetainedObjectCheck(
    reason: String,
    rescheduling: Boolean,
    delayMillis: Long = 0L
  ) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
      val scheduledIn = checkCurrentlyScheduledAt - SystemClock.uptimeMillis()
      SharkLog.d { "Ignoring request to check for retained objects ($reason), already scheduled in ${scheduledIn}ms" }
      return
    } else {
      val verb = if (rescheduling) "Rescheduling" else "Scheduling"
      val delay = if (delayMillis > 0) " in ${delayMillis}ms" else ""
      SharkLog.d { "$verb check for retained objects${delay} because $reason" }
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
    backgroundHandler.postDelayed({
      checkScheduledAt = 0
      checkRetainedObjects(reason)
    }, delayMillis)
  }

上面的方法就是再次检查内存泄漏。后面会继续调用。

private fun checkRetainedObjects(reason: String) {
    val config = configProvider()
    // A tick will be rescheduled when this is turned back on.
    if (!config.dumpHeap) {
      SharkLog.d { "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false" }
      return
    }

    var retainedReferenceCount = objectWatcher.retainedObjectCount

    if (retainedReferenceCount > 0) {
      gcTrigger.runGc()
      retainedReferenceCount = objectWatcher.retainedObjectCount
    }

    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

    if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
      onRetainInstanceListener.onEvent(DebuggerIsAttached)
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(
              R.string.leak_canary_notification_retained_debugger_attached
          )
      )
      scheduleRetainedObjectCheck(
          reason = "debugger is attached",
          rescheduling = true,
          delayMillis = WAIT_FOR_DEBUG_MILLIS
      )
      return
    }

    val now = SystemClock.uptimeMillis()
    val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
    if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
      onRetainInstanceListener.onEvent(DumpHappenedRecently)
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
      )
      scheduleRetainedObjectCheck(
          reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
          rescheduling = true,
          delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
      )
      return
    }

    SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" }
    dismissRetainedCountNotification()
    dumpHeap(retainedReferenceCount, retry = true)
  }

第一步,当存在的数量大于0时,先进行一次GC,这样还没有被释放的对象可以认为是泄露对象。并将留下来的对象个数赋值给retainedReferenceCount。

第二步:在第一步之后,存在对象的个数小于retainedVisibleThreshold 5个,且应用可见,或者不可见时间小于5s,退出,定时2s后再次检查存在对象

private fun checkRetainedCount(
    retainedKeysCount: Int,
    retainedVisibleThreshold: Int
  ): Boolean {
    val countChanged = lastDisplayedRetainedObjectCount != retainedKeysCount
    lastDisplayedRetainedObjectCount = retainedKeysCount
    if (retainedKeysCount == 0) {
    //存在对象为0,然后回调没有更多对象,并且返回true,在外面就会直接return
      SharkLog.d { "Check for retained object found no objects remaining" }
      if (countChanged) {
        onRetainInstanceListener.onEvent(NoMoreObjects)
        showNoMoreRetainedObjectNotification()
      }
      
      return true
    }

    if (retainedKeysCount < retainedVisibleThreshold) {
      if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
        if (countChanged) {
          onRetainInstanceListener.onEvent(BelowThreshold(retainedKeysCount))
        }
        showRetainedCountNotification(
            objectCount = retainedKeysCount,
            contentText = application.getString(
                R.string.leak_canary_notification_retained_visible, retainedVisibleThreshold
            )
        )
        scheduleRetainedObjectCheck(
            reason = "found only $retainedKeysCount retained objects (< $retainedVisibleThreshold while app visible)",
            rescheduling = true,
            delayMillis = WAIT_FOR_OBJECT_THRESHOLD_MILLIS
        )
        return true
      }
    }
    return false
  }

第三步,如果config里面配置的“调试时不允许dump heap”为false(默认值)且正在调试,退出,定时20s再次检查存在对象。

第四步,如果DumpHeap的时间间隔不足60s,退出,并等间隔时间达到60s再次检查存在对象。

第五步,执行了上面的四步之后,程序还没有return掉,则开始dumpHeap。

private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean
  ) {
    saveResourceIdNamesToMemory()
    val heapDumpUptimeMillis = SystemClock.uptimeMillis()
    KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
    val heapDumpFile = heapDumper.dumpHeap()
    if (heapDumpFile == null) {
      if (retry) {
        SharkLog.d { "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms" }
        scheduleRetainedObjectCheck(
            reason = "failed to dump heap",
            rescheduling = true,
            delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS
        )
      } else {
        SharkLog.d { "Failed to dump heap, will not automatically retry" }
      }
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(
              R.string.leak_canary_notification_retained_dump_failed
          )
      )
      return
    }
    lastDisplayedRetainedObjectCount = 0
    lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
    objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
    HeapAnalyzerService.runAnalysis(application, heapDumpFile)
  }

在这里生成Heap文件,然后分析Heap文件。

fun runAnalysis(
      context: Context,
      heapDumpFile: File
    ) {
      val intent = Intent(context, HeapAnalyzerService::class.java)
      intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
      startForegroundService(context, intent)
    }

会启动一个服务进行Heap文件分析。

override fun onHandleIntentInForeground(intent: Intent?) {
    if (intent == null || !intent.hasExtra(HEAPDUMP_FILE_EXTRA)) {
      SharkLog.d { "HeapAnalyzerService received a null or empty intent, ignoring." }
      return
    }

    // Since we're running in the main process we should be careful not to impact it.
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
    val heapDumpFile = intent.getSerializableExtra(HEAPDUMP_FILE_EXTRA) as File

    val config = LeakCanary.config
    val heapAnalysis = if (heapDumpFile.exists()) {
      analyzeHeap(heapDumpFile, config)
    } else {
      missingFileFailure(heapDumpFile)
    }
    onAnalysisProgress(REPORTING_HEAP_ANALYSIS)
    config.onHeapAnalyzedListener.onHeapAnalyzed(heapAnalysis)
  }
  
  private fun analyzeHeap(
    heapDumpFile: File,
    config: Config
  ): HeapAnalysis {
    val heapAnalyzer = HeapAnalyzer(this)

    //对混淆的代码进行支持
    val proguardMappingReader = try {
      ProguardMappingReader(assets.open(PROGUARD_MAPPING_FILE_NAME))
    } catch (e: IOException) {
      null
    }
    return heapAnalyzer.analyze(
        heapDumpFile = heapDumpFile,
        leakingObjectFinder = config.leakingObjectFinder,
        referenceMatchers = config.referenceMatchers,
        computeRetainedHeapSize = config.computeRetainedHeapSize,
        objectInspectors = config.objectInspectors,
        metadataExtractor = config.metadataExtractor,
        proguardMapping = proguardMappingReader?.readProguardMapping()
    )
  }
  
  
  fun analyze(
    heapDumpFile: File,
    leakingObjectFinder: LeakingObjectFinder,
    referenceMatchers: List<ReferenceMatcher> = emptyList(),
    computeRetainedHeapSize: Boolean = false,
    objectInspectors: List<ObjectInspector> = emptyList(),
    metadataExtractor: MetadataExtractor = MetadataExtractor.NO_OP,
    proguardMapping: ProguardMapping? = null
  ): HeapAnalysis {
    val analysisStartNanoTime = System.nanoTime()

    if (!heapDumpFile.exists()) {
      val exception = IllegalArgumentException("File does not exist: $heapDumpFile")
      return HeapAnalysisFailure(
          heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
          HeapAnalysisException(exception)
      )
    }

    return try {
      listener.onAnalysisProgress(PARSING_HEAP_DUMP)
      Hprof.open(heapDumpFile)
          .use { hprof ->
          
          //生成heap graph, 用于表示heap中的对象关系图
            val graph = HprofHeapGraph.indexHprof(hprof, proguardMapping)
            //初始化FindLeakInput
            val helpers =
              FindLeakInput(graph, referenceMatchers, computeRetainedHeapSize, objectInspectors)
            //分析heap graph
            helpers.analyzeGraph(
                metadataExtractor, leakingObjectFinder, heapDumpFile, analysisStartNanoTime
            )
          }
    } catch (exception: Throwable) {
      HeapAnalysisFailure(
          heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
          HeapAnalysisException(exception)
      )
    }
  }
  
    private fun FindLeakInput.analyzeGraph(...): HeapAnalysisSuccess {
    listener.onAnalysisProgress(EXTRACTING_METADATA)
    //提取graph的metadata数据
    val metadata = metadataExtractor.extractMetadata(graph)
    listener.onAnalysisProgress(FINDING_RETAINED_OBJECTS)
    //获取泄露对象的 objectIds
    val leakingObjectIds = leakingObjectFinder.findLeakingObjectIds(graph)
    //生成泄露对象报告
    val (applicationLeaks, libraryLeaks) = findLeaks(leakingObjectIds)

    return HeapAnalysisSuccess(...)
  }
  
  //生成泄露对象报告
  //共有两种泄漏类型:ApplicationLeak 和 LibraryLeak
  private fun FindLeakInput.findLeaks(leakingObjectIds: Set<Long>): Pair<List<ApplicationLeak>, List<LibraryLeak>> {
    val pathFinder = PathFinder(graph, listener, referenceMatchers)
    //查询泄露对象到 GC Roots 的路径
    val pathFindingResults =
      pathFinder.findPathsFromGcRoots(leakingObjectIds, computeRetainedHeapSize)
    return buildLeakTraces(pathFindingResults)
  }
FragmentDestroyWatcher.install
install(...) {
    //创建观察者集合
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
    //如果SDK大于等于O,则添加"android.app.Fragment"的观察者AndroidOFragmentDestroyWatcher
    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
          AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
      )
    }

    //对"androidx.fragment.app.Fragment"添加观察
    getWatcherIfAvailable(
        ANDROIDX_FRAGMENT_CLASS_NAME,
        ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
        objectWatcher,
        configProvider
    )?.let {
      //添加观察者AndroidXFragmentDestroyWatcher
      fragmentDestroyWatchers.add(it)
    }
    //对"android.support.v4.app.Fragment"添加观察
    getWatcherIfAvailable(
        ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
        ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
        objectWatcher,
        configProvider
    )?.let {
      //添加观察者AndroidSupportFragmentDestroyWatcher
      fragmentDestroyWatchers.add(it)
    }

    //如果观察列表为空,直接返回
    if (fragmentDestroyWatchers.size == 0) {
      return
    }

    //对所有Fragment进行监视
    application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityCreated(
        activity: Activity,
        savedInstanceState: Bundle?
      ) {
        for (watcher in fragmentDestroyWatchers) {
         //-->2.2.2触发观察者的invoke()方法
          watcher(activity)
        }
      }
    })
  }
  
  //添加观察 @AndroidXFragmentDestroyWatcher.kt
internal class AndroidXFragmentDestroyWatcher(
  private val objectWatcher: ObjectWatcher,
  private val configProvider: () -> Config
) : (Activity) -> Unit {

  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentCreated(
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?
    ) {
      ViewModelClearedWatcher.install(fragment, objectWatcher, configProvider)
    }

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null && configProvider().watchFragmentViews) {
        // -->2.1.2 objectWatcher监视view
        objectWatcher.watch(
            view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
            "(references to its views should be cleared to prevent leaks)"
        )
      }
    }

    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      if (configProvider().watchFragments) {
        // objectWatcher监视view
        objectWatcher.watch(
            fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
        )
      }
    }
  }

  //触发观察者的invoke()方法
  override fun invoke(activity: Activity) {
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      //注册监听fragment生命周期回调
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      ViewModelClearedWatcher.install(activity, objectWatcher, configProvider)
    }
  }
}

Watcher向Activity的FragmentManager注册FragmentLifecycleCallbacks,这样在Fragment调用onDestroyView和onDestory之后就能观察Fragment的View或者Fragment本身是否存在泄漏。将Fragment进行监视之后的流程同Activity,可以参考前面对activity的分析。

onAppWatcherInstalled
onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit

init {
    val internalLeakCanary = try {
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE")
          .get(null)
    } catch (ignored: Throwable) {
      NoLeakCanary
    }
    @kotlin.Suppress("UNCHECKED_CAST")
    onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit
  }

internalLeakCanary是一个方法对象,这个对象的值是通过反射获取的 InternalLeakCanary.INSTANCE ,它是一个单例对象。InternalLeakCanary 位于 leakcanary-android-core 模块,这也是需要反射的原因。

在install中调用onAppWatcherInstalled(application),

也就是onAppWatcherInstalled(application) 实际调用的是InternalLeakCanary.invoke方法:

override fun invoke(application: Application) {
    this.application = application
    //添加内存泄漏监听,方便objectWatcher通知InternalLeakCanary
    AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
    //初始化heapDumper,用于生成heap文件
    val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)
    //指定GC策略
    val gcTrigger = GcTrigger.Default

    val configProvider = { LeakCanary.config }
    //创建线程
    val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
    handlerThread.start()
    val backgroundHandler = Handler(handlerThread.looper)
    //初始化heapDumpTrigger
    heapDumpTrigger = HeapDumpTrigger(
        application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
        configProvider
    )
    //监听application是否可见
    application.registerVisibilityListener { applicationVisible ->
      this.applicationVisible = applicationVisible
      heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
    }
    //监听Activity Resume
    registerResumedActivityListener(application)
    //创建动态快捷方式
    addDynamicShortcut(application)

    disableDumpHeapInTests()
  }
小结

我们可以看到LeakCanary的核心逻辑在于利用ReferenceQueue对WeakReferences可达性改变进行监听,获取发送内存泄漏的对象,同时使用自己的Shark对Heap文件进行分析,该机制只对Java层起作用,而Native层并不在检查范围。

相关文章

  • LeakCanary 2.0源码分析与总结

    本文基于LeakCanary 2.0源码分析LeakCanary - 官方地址LeakCanary - GitHu...

  • LeakCanary 原理分析

    本文主要内容 1、Reference 简介 2、LeakCanary 使用 3、LeakCanary 源码分析 L...

  • LeakCanary原理

    一、源码 LeakCanary内存分析模块,独立进程,包名:leakcanary,保持app进程独立。在Appli...

  • leakcanary源码分析

    leakcanary LeakCananry 是 Square 公司开源的一个针对 Android 的内存泄漏检测...

  • LeakCanary源码分析

    前言 最近高产似母猪,闲下来的时候就喜欢找找源码看。昨天看了下LeakCanary,准备来分析一波。 导入 gra...

  • LeakCanary源码分析

    大家好,我是苍王。 以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。 [Androi...

  • leakcanary源码分析

    在Application中初始化 使用build设计模式,创建RefWatcher对象 设置heap监听 Serv...

  • LeakCanary 源码分析

    使用 LeakCanary 已经有了最新的2.0 版本,可以去获取最新版本https://github.com/s...

  • LeakCanary源码分析

    1 LeakCanary简介 LeakCanary 是 Square 公司的一个开源库。通过它可以在 App 运行...

  • leakcanary源码分析

    写在前面 leakcanary版本是2.4.0 用法很简单, 就不再说了.使用完后, 有几个问题 为何桌面会多出来...

网友评论

      本文标题:LeakCanary源码分析

      本文链接:https://www.haomeiwen.com/subject/kmvcaktx.html