每日一問:說說你對 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

相關文章
相關標籤/搜索