每日一问:说说你对 LeakCanary 的了解

昨天的问题说到了关于 内存泄漏须要注意的点,在文章最后有说到 LeakCanary 检测内存泄漏。实际上,我相信绝大多数人也知道甚至使用过这个库。java

这个系列一般来讲若是发现了不错的资源,会选择直接截取部分拿过来,因此对于文章底部的参考连接通常都是很是不错的,能够直接去看哟~android

LeakCanary 的基本工做流程是怎样的?

LeakCanary 的使用方式很是简单,只须要在 build.gradle 里面直接写上依赖,而且在 Application 类里面作注册就能够了。git

固然,须要在 Application 里面注册这样的操做仅在大多数人接触的 1.x 版本,实际上 LeakCanary 如今已经升级到了 2.x 版本,代码侵入性更低,并且纯 Kotlin 写法。从 Google 各类 Demo 主推 Kotlin 以及各类主流库都在使用 Kotlin 编写来看可见 Kotlin 确实在 Android 开发中愈发重要,没使用的小伙伴必须得去学习一波了,目前我也是纯 Kotlin 作开发的。github

对于工做原理我相信你们应该也是或多或少有必定了解,这里恰好有一张很是不错的流程图就直接借用过来了,另外他从源码角度理解 LeakCanary 的这篇文章也写的很是不错,感兴趣的点击文章底部的连接直达。app

初次使用 LeakCanary 为何没有 Icon 入口

咱们经常在使用 LeakCanary 的时候会发现这样一个问题:最开始并无出现 LeakCanary 的 Launcher icon,但当出现了内存泄漏警告的时候系统桌面就多了这么一个图标,通常状况下都是会很是好奇的。ide

从 1.x 的源码中就能够看出端倪。在 leakcanary-android 的 manifast 中,咱们能够看到相关配置:学习

<!--leakcanary-sample/src/main/AndroidManifest.xml-->
<service
    android:name=".internal.HeapAnalyzerService"
    android:process=":leakcanary"
    android:enabled="false"
    />
<service
    android:name=".DisplayLeakService"
    android:process=":leakcanary"
    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>

咱们能够看到 DisplayLeakActivity 被设置为了 Launcher,并设置上了对应的图标,因此咱们使用 LeakCanary 会在系统桌面上生成 Icon 入口。可是 DisplayLeakActivityenable 属性默认是 false,因此在桌面上是不会显示入口的。而在发生内存泄漏的时候,LeakCanary 会主动将 enable 属性置为 true。gradle

LeakCanary 2 都作了些什么

最近 LeakCanary 升级到了 2.x 版本,这是一次彻底的重构,去除了 1.x release 环境下引用的空包 leakcanary-android-no-op。而且 Kotlin 语言覆盖高达 99.8%,也不再须要在 Application 里面作相似下面的代码。ui

//com.example.leakcanary.ExampleApplication
@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);
}

只须要在依赖里面添加这样的代码就能够了。this

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-2'
}

初次看到这样的操做,会以为很是神奇,仔细阅读源码才回发现它居然使用了一个骚操做:ContentProvider

leakcanary-leaksentry 模块的 AndroidManifest.xml文件中能够看到:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.squareup.leakcanary.leaksentry"
    >

  <application>
    <provider
        android:name="leakcanary.internal.LeakSentryInstaller"
        android:authorities="${applicationId}.leak-sentry-installer"
        android:exported="false"/>
  </application>
</manifest>

再通过查看 LeakSentryInstaller 能够看到:

package leakcanary.internal

import android.app.Application
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import leakcanary.CanaryLog

/**
 * Content providers are loaded before the application class is created. [LeakSentryInstaller] is
 * used to install [leaksentry.LeakSentry] on application start.
 */
internal class LeakSentryInstaller : ContentProvider() {

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

  override fun query(
    uri: Uri,
    strings: Array<String>?,
    s: String?,
    strings1: Array<String>?,
    s1: String?
  ): Cursor? {
    return null
  }

  override fun getType(uri: Uri): String? {
    return null
  }

  override fun insert(
    uri: Uri,
    contentValues: ContentValues?
  ): Uri? {
    return null
  }

  override fun delete(
    uri: Uri,
    s: String?,
    strings: Array<String>?
  ): Int {
    return 0
  }

  override fun update(
    uri: Uri,
    contentValues: ContentValues?,
    s: String?,
    strings: Array<String>?
  ): Int {
    return 0
  }
}

确实是真的骚,咱们都知道 ContentProvideronCreate() 的调用时机介于 ApplicationattachBaseContext()onCreate() 之间,LeakCanary 这么作,把 init 的逻辑放到库内部,让调用方彻底不须要在 Application 里去进行初始化了,十分方便。这样下来既能够避免开发者忘记初始化致使一些错误,也可让咱们庞大的 Application 代码更加简洁。

参考:https://www.jianshu.com/p/49239eac7a76

相关文章
相关标签/搜索