我們一起來分析一下大名鼎鼎的 Leakcanary, 想必作為 Android 開發(fā)都多多少少接觸過,新版本的 Leakcanary 也用 Kotlin 重寫了一遍,最近詳細(xì)查看了下源碼,分享一下。
tips:本來是只想分析下內(nèi)存泄漏檢測部分,但寫著寫著就跑偏了,因?yàn)閮?nèi)存泄漏的檢測難點(diǎn)在于對對象生命周期的把控, Leakcanary 對于 Service 生命周期的把控我覺得非常值得我們學(xué)習(xí),并且在項(xiàng)目中也會用到。外加 Leakcanary 用 Kotlin 重寫,一些語法糖我平時也沒用過,就順便寫了下,整體讀下來有點(diǎn)啰嗦。
源碼版本
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
內(nèi)存泄漏
本博客著重分析 leakcanary 源碼實(shí)現(xiàn)原理以及一些優(yōu)秀設(shè)計(jì),對于內(nèi)存泄漏的解釋就簡單用我自己的理解來解釋下:長生命周期對象持有短生命周期對象,當(dāng)短生命周期對象需要被回收時因其被長生命周期對象持有導(dǎo)致無法正?;厥盏那闆r;
源碼淺析
在了解其優(yōu)秀設(shè)計(jì)之前先來簡單分析下其源碼以及實(shí)現(xiàn)原理。
如需完整版的學(xué)習(xí)資料 請點(diǎn)擊免費(fèi)領(lǐng)取
檢測原理
Leakcanary 檢測內(nèi)存泄漏的原理很簡單,就是利用弱引用 WeakReference 的雙參數(shù)構(gòu)造方法
WeakReference(T referent, ReferenceQueue<? super T> q)
來檢測被弱引用的對象是否被正常回收、釋放,舉個例子:
// 定義類
class A
// 檢測的目標(biāo)對象
val obj = A()
val queue = ReferenceQueue<A>()
val weakObj = WeakReference(obj, queue)
// 觸發(fā)gc回收(注意:這樣的操作不一定可以觸發(fā)gc,具體如何觸發(fā)gc 在下面的源碼分析中有提到 leakcanary 是如何觸發(fā)gc的)
System.gc()
val tmp = queue.poll()
if (tmp === obj) {
// 被回收
} else {
// 未回收
}
Android 開發(fā)中的 Activity、Fragment、Service、自定義 View 都是容易發(fā)生內(nèi)存泄漏的對象,Leakcanary 所做的工作就是在合適的時機(jī)(一般是在回收時,如 Activity 的 onDestory 后)對這些對象進(jìn)行弱引用并且關(guān)聯(lián)引用隊(duì)列,根據(jù)其是否被添加到引用隊(duì)列來判斷是否發(fā)生泄漏。
關(guān)于判斷一個對象是否發(fā)生泄漏的原理上面的示例代碼已經(jīng)簡單演示,下面我們就順著源碼來看看 Leakcanary 的實(shí)現(xiàn)細(xì)節(jié)。
初始化
Leakcanary 僅需引入依賴即可完成初始化,放到現(xiàn)在這也不算多么神奇的技巧了,這是利用了 ContentProvider。
ContentProvider 的初始化時機(jī)在 Application 的 onCreate 之前,并且在 ContentProvider 的 onCreate 方法中可以獲取到 context、applicationContext。
當(dāng)項(xiàng)目引入 Leakcanary 后打包出的 apk 的清單文件中可以找到注冊了MainProcessAppWatcherInstaller
,其關(guān)鍵源碼部分如下:
internal class MainProcessAppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
//..
}
可以看出調(diào)用了 AppWatcher.manualInstall()
進(jìn)行了初始化,其源碼如下:
AppWatcher.kt
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默認(rèn) 5s
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) // 獲取默認(rèn)Watchers 下面詳細(xì)分析
) {
// 檢查是否在主線程
// 原理:Looper.getMainLooper().thread === Thread.currentThread()
checkMainThread()
// ...
this.retainedDelayMillis = retainedDelayMillis
// 初始化 Shark 庫
if (application.isDebuggableBuild) {
LogcatSharkLog.install()
}
// 這行代碼下面詳細(xì)分析
LeakCanaryDelegate.loadLeakCanary(application)
// 對 watchers 遍歷調(diào)用 install
watchersToInstall.forEach {
it.install()
}
// 給 installCause 賦值,代表已經(jīng)初始化
installCause = RuntimeException("manualInstall() first called here")
}
appDefaultWatchers(application)
上述初始化方法中第三個參數(shù) watchersToInstall
被賦予了默認(rèn)值,通過 appDefaultWatchers
獲取了一個 List<InstallableWatcher>
,先看下 InstallableWatcher 源碼:
interface InstallableWatcher {
fun install()
fun uninstall()
}
是一個接口,定義了兩個方法看命名也能明白是安裝和卸載,接著看下 appDefaultWatchers 方法返回了什么:
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher // 注意這個 objectWatcher 很重要
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher), // 用于監(jiān)控activity內(nèi)存泄漏
FragmentAndViewModelWatcher(application, reachabilityWatcher),// 用于監(jiān)控fragment,viewmodel 內(nèi)存泄漏
RootViewWatcher(reachabilityWatcher),// 用于監(jiān)聽 rootview 內(nèi)存泄漏
ServiceWatcher(reachabilityWatcher) // 用于監(jiān)聽 service 內(nèi)存泄漏
)
}
注意上述方法的第二個參數(shù) reachabilityWatcher
默認(rèn)賦值了 objectWatcher
:
val objectWatcher = ObjectWatcher(...)
先記住他是 ObjectWatcher 類的實(shí)例,并且將其傳遞給了用于檢測的各個 InstallableWatcher 實(shí)現(xiàn)類。
LeakCanaryDelegate.loadLeakCanary(application)
接著再回過頭來看一下 LeakCanaryDelegate.loadLeakCanary(application)
這句代碼,loadLeakCanary 作為 LeakCanaryDelegate 類中的一個函數(shù)類型變量,所以可以直接調(diào)用,看一下其源碼:文章來源:http://www.zghlxwxcb.cn/news/detail-433542.html
internal object LeakCanaryDelegate {
// 延遲初始化
val loadLeakCanary by lazy {
try {
// 默認(rèn)加載 InternalLeakCanary 獲取其 INSTANCE 字段
// InternalLeakCanary 是一個 object class,編譯為 java 后會自動生成 INSTANCE
// 就是一個單例類 這里是獲取其單例對象
val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
leakCanaryListener.getDeclaredField("INSTANCE")
.get(null) as (Application) -> Unit
} catch (ignored: Throwable) {
// 出現(xiàn)異常時返回 NoLeakCanary
NoLeakCanary
}
}
// (Application) -> Unit 函數(shù)類型變量,接受一個 application 作為參數(shù)
// 內(nèi)部都是空實(shí)現(xiàn)
object NoLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
override fun invoke(application: Application) {
}
override fun onObjectRetained() {
}
}
}
一般情況下 LeakCanaryDelegate.loadLeakCanary(application) 就相當(dāng)于調(diào)用了 InternalLeakCanary,當(dāng)然 InternalLeakCanary 也是 (Application) -> Unit 的實(shí)現(xiàn)了,對你沒看錯,函數(shù)類型不僅可以聲明變量,也可以定義實(shí)現(xiàn)類,但需要實(shí)現(xiàn) invoke
方法,看下其源碼:文章來源地址http://www.zghlxwxcb.cn/news/detail-433542.html
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
override fun invoke(application
到了這里,關(guān)于Android 源碼淺析:Leakcanary 內(nèi)存泄漏檢測的好幫手的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!