我根据 Android 文档编写了一个基本的 绑定服务 ,但 LeakCanary 告诉我该服务正在泄漏。
class LocalService : Service() {
private val binder = LocalBinder()
private val generator = Random()
val randomNumber: Int
get() = generator.nextInt(100)
inner class LocalBinder : Binder() {
fun getService(): LocalService = this@LocalService
}
override fun onBind(intent: Intent): IBinder {
return binder
}
override fun onDestroy() {
super.onDestroy()
LeakSentry.refWatcher.watch(this) // Only modification is to add LeakCanary
}
}
如果我从以下活动绑定到服务,LeakCanary 会检测到服务已泄漏
class MainActivity: Activity() {
private var service: LocalService? = null
private val serviceConnection = object: ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
service = (binder as LocalBinder).getService()
}
override fun onServiceDisconnected(name: ComponentName?) {
service = null
}
}
override fun onStart() {
super.onStart()
bindService(Intent(this, LocalService::class.java), serviceConnection, BIND_AUTO_CREATE)
}
override fun onStop() {
super.onStop()
service?.let {
unbindService(serviceConnection)
service = null
}
}
}
┬
├─ com.example.serviceleak.LocalService$LocalBinder
│ Leaking: NO (it's a GC root)
│ ↓ LocalService$LocalBinder.this$0
│ ~~~~~~
╰→ com.example.serviceleak.LocalService
Leaking: YES (RefWatcher was watching this)
我也对 LeakCanary 感到困惑。如果你转储堆并导入Android Studio,你会发现实例和相关对象确实存在,但AS没有显示内存泄漏(“0个类”??):
现在我找到了更好的方法来解决这个问题。使用 static 内部类可以避免隐式持有外部类实例,而是使用
WeakReference
: 来持有它
class MyService : Service() {
private val binder by lazy { MyServiceBinder(this) }
override fun onBind(intent: Intent): IBinder {
return binder
}
// ...
companion object {
class MyServiceBinder(service: MyService) : Binder() {
private val serviceRef: WeakReference<MyService> = WeakReference(service)
fun getService(): MyService? = serviceRef.get()
}
}
}
执行此操作后,leakcanary 不会显示警告,堆转储也不会显示剩余实例和其他相关实例: