昨天的問題說到了關於 內存泄漏須要注意的點,在文章最後有說到 LeakCanary 檢測內存泄漏。實際上,我相信絕大多數人也知道甚至使用過這個庫。java
這個系列一般來講若是發現了不錯的資源,會選擇直接截取部分拿過來,因此對於文章底部的參考連接通常都是很是不錯的,能夠直接去看喲~android
LeakCanary 的使用方式很是簡單,只須要在 build.gradle 裏面直接寫上依賴,而且在 Application 類裏面作註冊就能夠了。git
固然,須要在 Application 裏面註冊這樣的操做僅在大多數人接觸的 1.x 版本,實際上 LeakCanary 如今已經升級到了 2.x 版本,代碼侵入性更低,並且純 Kotlin 寫法。從 Google 各類 Demo 主推 Kotlin 以及各類主流庫都在使用 Kotlin 編寫來看可見 Kotlin 確實在 Android 開發中愈發重要,沒使用的小夥伴必須得去學習一波了,目前我也是純 Kotlin 作開發的。github
對於工做原理我相信你們應該也是或多或少有必定了解,這裏恰好有一張很是不錯的流程圖就直接借用過來了,另外他從源碼角度理解 LeakCanary 的這篇文章也寫的很是不錯,感興趣的點擊文章底部的連接直達。app
咱們經常在使用 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 入口。可是 DisplayLeakActivity
的 enable
屬性默認是 false,因此在桌面上是不會顯示入口的。而在發生內存泄漏的時候,LeakCanary 會主動將 enable
屬性置爲 true。gradle
最近 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 } }
確實是真的騷,咱們都知道 ContentProvider
的 onCreate()
的調用時機介於 Application
的 attachBaseContext()
和 onCreate()
之間,LeakCanary 這麼作,把 init 的邏輯放到庫內部,讓調用方徹底不須要在 Application
裏去進行初始化了,十分方便。這樣下來既能夠避免開發者忘記初始化致使一些錯誤,也可讓咱們龐大的 Application
代碼更加簡潔。