【问题标题】:Bound service leaks memory绑定服务泄漏内存
【发布时间】:2019-05-22 08:15:25
【问题描述】:

我写了一个基本的bound service based on the Android documentation,但 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)

【问题讨论】:

  • Context 类中没有任何标志 BIND_AUTO_START...为什么要分别在 onCreate - onDestroy 方法中绑定 - 取消绑定服务?
  • 你是对的,这是一个错字。标志应该是BIND_AUTO_CREATE。在 onCreate 和 onDestroy 中服务是否绑定和未绑定无关;无论您使用哪种生命周期方法来绑定和取消绑定,它都会泄漏。但我会更改我的示例以匹配 Android 文档。
  • 你能不能尝试...将 Binder 类移动到它自己的独立类而不是内部类,并将随机数生成器移动到该类。然后在服务内部,将它实例化为一个字段,并在 onBind() 中返回它。我在想也许 LeakCanary 认为这是一个泄漏,因为活页夹内部类持有对外部类(服务)的引用,并且对活页夹的引用被另一个具有它自己的生命周期(活动)的组件持有。
  • 内部类是问题所在:stackoverflow.com/a/49365796/5823014
  • @JánosSicz-Mesziár 我同意,但是一旦服务被销毁,就不应该持有对活页夹的引用

标签: android android-service android-service-binding leakcanary


【解决方案1】:

我不知道现在回答是否晚了,但在阅读了您的问题后,我还在我的项目中设置了leakCanary 并发现了这个泄漏。我确信这是因为内部绑定器类持有外部类的引用,这里是服务。这就是为什么在您的泄漏日志中它显示 LocationService 正在泄漏。 我通过@commonsguy here 找到了一个解决方案,并通过一个更简单的示例here 实现了该解决方案。希望这可以帮助。继续编码,保持幸福。

【讨论】:

  • 提问者修复泄漏的代码有何重大变化?
  • 如果活页夹是外部的,你应该如何访问服务对象?作为内部类的活页夹在 android 文档中也是如此。
  • @Sujit:我可能会找到解决方案。在活页夹实例中保留对服务的显式引用。当服务正在销毁时,显式销毁活页夹实例并将服务引用设置为 null。
猜你喜欢
  • 2015-06-29
  • 2011-02-18
  • 1970-01-01
  • 2015-02-12
  • 2019-12-18
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多