首页 > 解决方案 > 数据绑定内存泄漏

问题描述

当我打开使用数据绑定的片段时,发生内存泄漏,如下所示。有没有合适的答案来解决这个问题?

我用了

┬───
│ GC Root: System class
│
├─ androidx.databinding.ViewDataBinding class
│    Leaking: NO (a class is never leaking)
│    ↓ static ViewDataBinding.sReferenceQueue
│                             ~~~~~~~~~~~~~~~
├─ java.lang.ref.ReferenceQueue instance
│    Leaking: UNKNOWN
│    ↓ ReferenceQueue.head
│                     ~~~~
├─ androidx.databinding.ViewDataBinding$WeakListener instance
│    Leaking: UNKNOWN
│    ↓ ViewDataBinding$WeakListener.mObservable
│                                   ~~~~~~~~~~~
├─ androidx.databinding.ViewDataBinding$LiveDataListener instance
│    Leaking: UNKNOWN
│    ↓ ViewDataBinding$LiveDataListener.mLifecycleOwner
│                                       ~~~~~~~~~~~~~~~
╰→ com.norm.news.ui.source.NewsSourceFragment instance
​     Leaking: YES (ObjectWatcher was watching this because com.norm.news.ui.source.NewsSourceFragment received Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
​     key = 55b68d7d-56bb-46ff-ad17-e29f01b6a808
​     watchDurationMillis = 5207
​     retainedDurationMillis = 139
​     key = 2fc4bd04-641e-4bea-8e13-71650f6e2a25

METADATA

Build.VERSION.SDK_INT: 24
Build.MANUFACTURER: samsung
LeakCanary version: 2.1
App process name: com.norm.news.debug
Analysis duration: 12283 ms

这是我的片段中的片段代码。

此代码引用自Android Kotlin Fundamentals 05.3: Data binding with ViewModel and LiveData

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding: FragmentNewsSourceBinding = DataBindingUtil.inflate(
            inflater,
            R.layout.fragment_news_source,
            container,
            false
        )
        // Specify the current activity as the lifecycle owner of the binding.
        // This is necessary so that the binding can observe LiveData updates.
        binding.lifecycleOwner = this

        val application = requireNotNull(this.activity).application
        val viewModelFactory = NewsSourceViewModelFactory(application)
        val newsSourceViewModel =
            ViewModelProviders.of(this, viewModelFactory).get(NewsSourceViewModel::class.java)
        binding.newsSourceViewModel = newsSourceViewModel

        binding.rvSourceLists.adapter = NewsSourceAdapter(NewsSourceAdapter.OnClickListener {
            newsSourceViewModel.displayNewsSourceDetails(it)
        })

        newsSourceViewModel.navigateToSelectedItem.observe(viewLifecycleOwner, Observer {
            if (it != null) {
                this.findNavController()
                    .navigate(NewsSourceFragmentDirections.actionNewsSourceFragmentToNewsFragment(it.id))
                newsSourceViewModel.displayNewsSourceDetailsComplete()
            }
        })

        return binding.root
    }

标签: androidmemory-leaksleakcanary

解决方案


基于读取泄漏跟踪和源代码,这看起来像是数据绑定库中的一个错误。我不确定是什么触发了它。

有一个引用队列,当弱引用的实例弱可达时,WeakListener 实例被添加到其中。所以我们知道在这种情况下,这就是监听器所处的状态。WeakRefs enqueued 将一直留在那里,直到通过消费代码(这里是数据绑定库)处理队列,这通常不是问题,因为 ref 为空。然而,这里的weakref有一个mObservable字段,它最终会泄漏一个生命周期所有者,它是一个被破坏的片段。这将在下一次处理 ref 队列时停止泄漏,尽管尚不清楚何时发生。

所以这看起来像是数据绑定库如何处理弱引用的设计缺陷/错误。

您可能应该使用此上下文向该项目提交错误,并提供额外的详细信息,例如 lib 版本号。提供堆转储(易于从泄漏卡纳利共享)也会有所帮助。


推荐阅读