绑定服务泄漏内存

问题描述 投票:0回答:2

我根据 Android 文档编写了一个基本的 绑定服务 ,但 LeakCanary 告诉我该服务正在泄漏。

  1. 是否存在泄漏或者我是否错误配置了 LeakCanary?
  2. 如何编写一个不泄漏的绑定服务?

代码

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)
android android-service android-service-binding leakcanary
2个回答
9
投票

我不知道回答是否晚了,但在阅读了你的问题后,我还在我的项目中设置了leakCanary,并发现了这个泄漏。我确信这是因为内部活页夹类保存了此处提供服务的外部类的引用。这就是为什么在您的泄漏日志中显示 LocationService 正在泄漏。 我在@commonsguyhere找到了一个解决方案,并用一个更简单的示例here实现了该解决方案。希望这有帮助。继续编码,保持祝福。


0
投票

我也对 LeakCanary 感到困惑。如果你转储堆并导入Android Studio,你会发现实例和相关对象确实存在,但AS没有显示内存泄漏(“0个类”??):

图片:此处有 28 个分配

现在我找到了更好的方法来解决这个问题。使用 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 不会显示警告,堆转储也不会显示剩余实例和其他相关实例:

图片:此处有 10 个分配

© www.soinside.com 2019 - 2024. All rights reserved.