有名な Leakcanary を一緒に分析しましょう。Android 開発者であれば、ある程度は触れたことがあるはずです。Leakcanary の新バージョンも Kotlin で書き直されました。最近ソースコードを詳しく確認して共有しました。
ヒント: 元々はメモリ リーク検出の部分を分析したかっただけですが、メモリ リークの検出の難しさはオブジェクトのライフ サイクルの制御にあるため、書いているうちに横道にそれてしまいました。サービスのライフ サイクルを制御するために Leakcanary は非常に価値があると思います私たちはそれを学び、プロジェクトに使用します。さらに、Leakcanary は Kotlin で書き直されています。糖衣構文を使ったことがないので、ただ書き留めただけです。全体を読むと少し冗長です。
ソースコードのバージョン
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
メモリーリーク
このブログでは、リークカナリーのソース コードの実装原理といくつかの優れた設計の分析に焦点を当てています。メモリ リークの説明は、ライフ サイクルの長いオブジェクトがライフ サイクルの短いオブジェクトを保持するという私の理解で簡単に説明されています。ライフ サイクルの短いオブジェクトが必要な場合リサイクルされる。ライフサイクルの長い物体によって保持されており、通常はリサイクルできない。
ソースコード分析
その優れた設計を理解する前に、そのソース コードと実装原理を簡単に分析してみましょう。
学習教材の完全版が必要な場合は、クリックして無料で入手してください
検出原理
メモリ リークを検出する Leakcanary の原理は非常に単純で、WeakReference の 2 つのパラメータ構築メソッドを使用することです。
WeakReference(T referent, ReferenceQueue<? super T> q)
弱参照オブジェクトが正常にリサイクルおよび解放されているかどうかを検出するには、たとえば次のようにします。
// 定义类
class A
// 检测的目标对象
val obj = A()
val queue = ReferenceQueue<A>()
val weakObj = WeakReference(obj, queue)
// 触发gc回收(注意:这样的操作不一定可以触发gc,具体如何触发gc 在下面的源码分析中有提到 leakcanary 是如何触发gc的)
System.gc()
val tmp = queue.poll()
if (tmp === obj) {
// 被回收
} else {
// 未回收
}
Android 開発におけるアクティビティ、フラグメント、サービス、およびカスタム ビューはすべてメモリ リークを起こしやすいオブジェクトです。Leakcanary が行うのは、適切なタイミング (通常はリサイクル中、アクティビティの onDestory 後など) でこれらのオブジェクトを弱めることです。参照キューを参照して関連付けます。を参照し、参照キューに追加されるかどうかに基づいてリークが発生したかどうかを判断します。
上記のサンプル コードでは、オブジェクトがリークされているかどうかを判断する原理を簡単に示しましたが、ソース コードに従って Leakcanary の実装の詳細を見てみましょう。
初期化
Leakcanary は初期化を完了するために依存関係を導入するだけで済みますが、これは魔法のトリックではなく、ContentProvider を使用します。
ContentProviderの初期化時刻はApplicationのonCreateより前であり、ContextとapplicationContextはContentProviderのonCreateメソッドで取得できます。
プロジェクトに Leakcanary が導入されると、パッケージ化された apk のマニフェスト ファイルに登録が見つかりますMainProcessAppWatcherInstaller
。主要なソース コード部分は次のとおりです。
internal class MainProcessAppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
//..
}
初期化のために呼び出されることがわかりAppWatcher.manualInstall()
、そのソースコードは次のとおりです。
AppWatcher.kt
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默认 5s
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) // 获取默认Watchers 下面详细分析
) {
// 检查是否在主线程
// 原理:Looper.getMainLooper().thread === Thread.currentThread()
checkMainThread()
// ...
this.retainedDelayMillis = retainedDelayMillis
// 初始化 Shark 库
if (application.isDebuggableBuild) {
LogcatSharkLog.install()
}
// 这行代码下面详细分析
LeakCanaryDelegate.loadLeakCanary(application)
// 对 watchers 遍历调用 install
watchersToInstall.forEach {
it.install()
}
// 给 installCause 赋值,代表已经初始化
installCause = RuntimeException("manualInstall() first called here")
}
appDefaultWatchers(アプリケーション)
上記の初期化メソッドの 3 番目のパラメーターにはwatchersToInstall
デフォルト値が与えられており、appDefaultWatchers
その値は によって取得されますList<InstallableWatcher>
。まず、InstallableWatcher のソース コードを見てみましょう。
interface InstallableWatcher {
fun install()
fun uninstall()
}
これは 2 つのメソッドを定義するインターフェイスです。名前を見るとインストールとアンインストールが理解できます。次に、appDefaultWatchers メソッドが何を返すかを見てみましょう。
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher // 注意这个 objectWatcher 很重要
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher), // 用于监控activity内存泄漏
FragmentAndViewModelWatcher(application, reachabilityWatcher),// 用于监控fragment,viewmodel 内存泄漏
RootViewWatcher(reachabilityWatcher),// 用于监听 rootview 内存泄漏
ServiceWatcher(reachabilityWatcher) // 用于监听 service 内存泄漏
)
}
上記のメソッドの 2 番目のパラメータにはreachabilityWatcher
デフォルトで値が割り当てられていることに注意してくださいobjectWatcher
。
val objectWatcher = ObjectWatcher(...)
まず、これは ObjectWatcher クラスのインスタンスであり、検出のために各 InstallableWatcher 実装クラスに渡されることに注意してください。
LeakCanaryDelegate.loadLeakCanary(アプリケーション)
次に戻ってLeakCanaryDelegate.loadLeakCanary(application)
このコードを見てください。loadLeakCanary は LeakCanaryDelegate クラスの関数型変数であるため、直接呼び出すことができます。そのソース コードを見てください。
internal object LeakCanaryDelegate {
// 延迟初始化
val loadLeakCanary by lazy {
try {
// 默认加载 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) {
// 出现异常时返回 NoLeakCanary
NoLeakCanary
}
}
// (Application) -> Unit 函数类型变量,接受一个 application 作为参数
// 内部都是空实现
object NoLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
override fun invoke(application: Application) {
}
override fun onObjectRetained() {
}
}
}
通常の状況では、LeakCanaryDelegate.loadLeakCanary(application) は InternalLeakCanary を呼び出すのと同等です。もちろん、InternalLeakCanary は (Application) -> Unit の実装でもあります。その通りです。関数型は変数を宣言するだけでなく、実装クラスも定義できますただし、invoke
メソッドを実装するには、そのソース コードを確認する必要があります。
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
override fun invoke(application