Android內(nèi)存泄漏檢測(cè)工具LeakCanary
背景
在Android應(yīng)用中,一個(gè)好的產(chǎn)品,除了功能強(qiáng)大,好的性能也是必不可少的。有調(diào)查顯示,近90%的受訪者會(huì)因?yàn)锳pp卡頓,內(nèi)存大等問(wèn)題而卸載該應(yīng)用,因此手機(jī)的性能問(wèn)題會(huì)影響用戶的體驗(yàn),如果用戶覺(jué)得該應(yīng)用的體驗(yàn)度不好,會(huì)直接卸載或切換其他平臺(tái)。
對(duì)于性能優(yōu)化,很多大公司會(huì)專門招聘性能優(yōu)化的人員。也有些初級(jí)工程師會(huì)接觸到這部分的工作,但是無(wú)從下手,對(duì)專業(yè)工具和專業(yè)代碼使用以及分析比較吃力,排查起來(lái)也比較費(fèi)勁。如果有專業(yè)的工具能夠只管的把這些記錄并標(biāo)記好。這樣初級(jí)工程師也可以通過(guò)詳情的問(wèn)題去排查,那么LeaksCanary就是這款工具了。
LeaksCanary 介紹
LeakCanary是Square公司為Android開(kāi)發(fā)者提供的一個(gè)自動(dòng)檢測(cè)內(nèi)存泄漏的工具。
LeakCanary本質(zhì)上是一個(gè)基于MAT進(jìn)行Android應(yīng)用程序內(nèi)存泄漏自動(dòng)化檢測(cè)的的開(kāi)源工具,我們可以通過(guò)集成LeakCanary提供的jar包到自己的工程中,一旦檢測(cè)到內(nèi)存泄漏,LeakCanary就會(huì)dump Memory信息,并通過(guò)另一個(gè)進(jìn)程分析內(nèi)存泄漏的信息并展示出來(lái),隨時(shí)發(fā)現(xiàn)和定位內(nèi)存泄漏問(wèn)題,而不用每次在開(kāi)發(fā)流程中都抽出專人來(lái)進(jìn)行內(nèi)存泄漏問(wèn)題檢測(cè),極大地方便了Android應(yīng)用程序的開(kāi)發(fā)。
使用方法
1.LeakCanary 如何自動(dòng)初始化
LeakCanary只需添加依賴就可以實(shí)現(xiàn)自動(dòng)初始化。LeakCanary是通過(guò)ContentProvider實(shí)現(xiàn)初始化的,在ContentProvider 的 onCreate方法中初始化LeakCanary。并且MainProcessAppWatcherInstaller是在主線程中初始化的。注意:ContentProvider的初始化是在Application的onCreate之前完成的,所以LeakCanary的初始化方法AppWatcher.manualInstall(application)也是在Application的onCreate之前完成的。
internal class MainProcessAppWatcherInstaller : ContentProvider() { override fun onCreate(): Boolean { val application = context!!.applicationContext as Application AppWatcher.manualInstall(application) return true } ... ... }
2.LeakCanary如何檢測(cè)內(nèi)存泄漏
2.1LeakCanary初始化時(shí)做了什么
AppWatcher.kt
@JvmOverloads fun manualInstall( application: Application, retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) ) { checkMainThread() if (isInstalled) { throw IllegalStateException( "AppWatcher already installed, see exception cause for prior install call", installCause ) } check(retainedDelayMillis >= 0) { "retainedDelayMillis $retainedDelayMillis must be at least 0 ms" } installCause = RuntimeException("manualInstall() first called here") this.retainedDelayMillis = retainedDelayMillis if (application.isDebuggableBuild) { LogcatSharkLog.install() } // Requires AppWatcher.objectWatcher to be set LeakCanaryDelegate.loadLeakCanary(application) watchersToInstall.forEach { it.install() } }
fun appDefaultWatchers( application: Application, reachabilityWatcher: ReachabilityWatcher = objectWatcher ): List<InstallableWatcher> { return listOf( ActivityWatcher(application, reachabilityWatcher), FragmentAndViewModelWatcher(application, reachabilityWatcher), RootViewWatcher(reachabilityWatcher), ServiceWatcher(reachabilityWatcher) ) }
在appDefaultWatchers方法中,會(huì)默認(rèn)初始化一些Watcher,在默認(rèn)情況下,我們只會(huì)監(jiān)控Activity,Fragment,RootView,Service這些對(duì)象是否泄漏。
2.2LeakCanary如何觸發(fā)檢測(cè)
以ActivityWatcher為例:
/** * Expects activities to become weakly reachable soon after they receive the [Activity.onDestroy] * callback. */ class ActivityWatcher( private val application: Application, private val reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher { private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() { override fun onActivityDestroyed(activity: Activity) { reachabilityWatcher.expectWeaklyReachable( activity, "${activity::class.java.name} received Activity#onDestroy() callback" ) } } override fun install() { application.registerActivityLifecycleCallbacks(lifecycleCallbacks) } override fun uninstall() { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks) } }
在Activity.onDestory時(shí),就會(huì)觸發(fā)檢測(cè)內(nèi)存泄漏。通過(guò)ActivityLifecycleCallbacks監(jiān)聽(tīng)生命周期變化,在onActivityDestroyed方法中調(diào)用ReachabilityWatcher的expectWeaklyReachable方法。
2.3LeakCanary如何檢測(cè)泄漏的對(duì)象
以Activity為例,通過(guò)ReachabilityWatcher的expectWeaklyReachable方法檢測(cè)。
fun interface ReachabilityWatcher { /** * Expects the provided [watchedObject] to become weakly reachable soon. If not, * [watchedObject] will be considered retained. */ fun expectWeaklyReachable( watchedObject: Any, description: String ) } ObjectWatcher.kt ObjectWatcher實(shí)現(xiàn)ReachabilityWatcher接口。 private val watchedObjects = mutableMapOf() private val queue = ReferenceQueue() @Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String ) { if (!isEnabled()) { return } removeWeaklyReachableObjects() val key = UUID.randomUUID() .toString() val watchUptimeMillis = clock.uptimeMillis() val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue) SharkLog.d { "Watching " + (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") + (if (description.isNotEmpty()) " ($description)" else "") + " with key $key" } watchedObjects[key] = reference checkRetainedExecutor.execute { moveToRetained(key) } }
1.通過(guò)觀察的實(shí)例watchedObject構(gòu)建弱引用KeyedWeakReference實(shí)例,watchedObject與ReferenceQueue關(guān)聯(lián),當(dāng)對(duì)象被回收時(shí),該弱引用對(duì)象將被存入ReferenceQueue當(dāng)中。
2.弱引用KeyedWeakReference實(shí)例會(huì)被被存儲(chǔ)在watchedObjects中(Map)。
3.檢測(cè)過(guò)程中,會(huì)調(diào)用removeWeaklyReachableObjects,將已回收對(duì)象從watchedObjects中移除。
4.如果watchedObjects中沒(méi)有移除對(duì)象,證明它沒(méi)有被回收,那么就會(huì)調(diào)用moveToRetained。
private fun removeWeaklyReachableObjects() { // WeakReferences are enqueued as soon as the object to which they point to becomes weakly // reachable. This is before finalization or garbage collection has actually happened. var ref: KeyedWeakReference? do { ref = queue.poll() as KeyedWeakReference? if (ref != null) { watchedObjects.remove(ref.key) } } while (ref != null) }
@Synchronized private fun moveToRetained(key: String) { removeWeaklyReachableObjects() val retainedRef = watchedObjects[key] if (retainedRef != null) { retainedRef.retainedUptimeMillis = clock.uptimeMillis() onObjectRetainedListeners.forEach { it.onObjectRetained() } } }
2.4弱引用 WeakReference
只要 GC 發(fā)現(xiàn)一個(gè)對(duì)象只有弱引用,則就會(huì)回收此弱引用對(duì)象。
public class WeakReference<T> extends Reference<T> { public WeakReference(T referent) { super(referent); } public WeakReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); } }
var str: Any? = Any() val quque = ReferenceQueue<Any>() val weakReference = WeakReference<Any>(str, quque) val weakReference_before_gc = weakReference.get() Log.v("reference_tag", weakReference_before_gc.toString()) str = null System.gc() Handler().postDelayed( { val weakReference_after_gc = weakReference.get() Log.v("reference_tag", weakReference_after_gc.toString()) }, 2000)
到此這篇關(guān)于Android內(nèi)存泄漏檢測(cè)工具LeakCanary的文章就介紹到這了,更多相關(guān)Android LeakCanary內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android使用MediaRecorder類實(shí)現(xiàn)視頻和音頻錄制功能
Android提供了MediaRecorder這一個(gè)類來(lái)實(shí)現(xiàn)視頻和音頻的錄制功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-07-07Android viewpage實(shí)現(xiàn)可控制的禁止滑動(dòng)
這篇文章主要為大家詳細(xì)介紹了Android viewpage實(shí)現(xiàn)可控制的禁止滑動(dòng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Android內(nèi)部存儲(chǔ)與外部存儲(chǔ)的示例講解
內(nèi)部存儲(chǔ)和外部存儲(chǔ)的概念隨著Android版本的更新也在發(fā)生不斷的變化。最早的內(nèi)部存儲(chǔ)指的是系統(tǒng)自帶的ROM存儲(chǔ),外部存儲(chǔ)指的是外置的Sdcard或者通過(guò)OTG掛在的USB存儲(chǔ)2023-03-03Android獲取清單文件中的meta-data,解決碰到數(shù)值為null的問(wèn)題
這篇文章主要介紹了Android獲取清單文件中的meta-data,解決碰到數(shù)值為null的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03Flexbox+ReclyclerView實(shí)現(xiàn)流式布局
這篇文章主要為大家詳細(xì)介紹了Flexbox+ReclyclerView實(shí)現(xiàn)流式布局,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11Android中Activity常用功能設(shè)置小結(jié)(包括全屏、橫豎屏等)
這篇文章主要介紹了Android中Activity常用功能設(shè)置小結(jié)(包括全屏、橫豎屏等),以簡(jiǎn)單實(shí)例形式分析了Android實(shí)現(xiàn)全屏、豎屏及一直顯示等的技巧與注意事項(xiàng),需要的朋友可以參考下2015-10-10Android彈出dialog后無(wú)法捕捉back鍵的解決方法
這篇文章主要為大家詳細(xì)介紹了Android彈出dialog后無(wú)法捕捉back鍵的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Android獲取本機(jī)電話號(hào)碼的簡(jiǎn)單方法
Android獲取本機(jī)電話號(hào)碼的簡(jiǎn)單方法,需要的朋友可以參考一下2013-05-05