天天看點

LeakCanary2的免寫 初始化代碼 原理Getting started Add LeakCanary to your build.gradle:ContentProviderref

最近LeakCanary做了更新,釋出了2.0版本,帶了了很多性能上的優化,不過一個很吸引我的點在于,他居然不像以前一樣,需要手動初始化了。

按照以前的使用流程,一般我們都是在dependencies 加入依賴

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
  releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
  // Optional, if you use support library fragments:
  debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
}	
           

接着在我們的application裡面加入初始化的邏輯。

public class ExampleApplication extends Application {
	
	  @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);
	    // Normal app init code...
	  }
	}
           

但是,新版本的 LeakCanary 2.0居然可以不再需要寫這個操作,隻需要在代碼裡面加入這麼一句依賴就可以了

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

我有點懷疑自己的眼睛,重新看了下他們的Readme,是不是真的

Getting started Add LeakCanary to your

build.gradle:

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

You’re good to go! LeakCanary will automatically show a notification when an activity or fragment memory leak is detected in

your debug build.

好吧,确實是這樣,那麼到底怎麼做到的?很神奇啊,這怎麼也會有一個地方會需要初始化的,到底換到那裡去了?

在經過對源碼的解讀後,發現了一個騷操作,感覺傳開後,以後的sdk庫都可能這麼做,教壞小朋友了。

ContentProvider

在經過對源碼的閱讀後,發現其實人家是基于CP這個對于絕大數開發來說,基本沒用到的四大元件之一來做的,真的是服了哈哈。檢視他的

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

這個類到底做了什麼。

internal class LeakSentryInstaller : ContentProvider() {

  override fun onCreate(): Boolean {
    CanaryLog.logger = DefaultCanaryLog()
    val application = context!!.applicationContext as Application
    InternalLeakSentry.install(application)  
    //騷操作在這裡,利用系統自動調用CP的onCreate方法來做初始化
    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
  }
}
           

我們看到這個CP類,沒做任何的CURD操作,全是空的,就純粹利用系統會回調這個接口來做初始化,幫助開發偷懶,省去調用寫初始化邏輯。

個人看待這個,覺得得分兩部門

好處:确實帶來了

"免侵入"

,不需要業務人員寫任何代碼。

壞處:這有點把CP給用歪了。以後所有人都這麼弄,接入的sdk都這麼寫的話,那就真的可愛了。

ref

  1. https://github.com/square/leakcanary/wiki/Migrating-to-LeakCanary-2.0