【发布时间】:2021-08-09 16:10:54
【问题描述】:
这里发生了几件事:
- 我尝试使用 Coil / Glide,都遇到同样的问题 -> 我怀疑问题不在于图像加载本身,而在于视图持有者的回收过程
- LeakCanary 未报告任何内存泄漏
- 我使用 Lru 缓存通过他们的查找键存储联系人照片 -> 缓存图像由 Coil / Glide 加载,但仍分配在堆上(同一个图像现在在堆上出现两次)
看起来是这样的:
如您所见,我有超过 1000 个位图分配在堆上占用了高达 123MB 的空间,即使我的列表只包含大约 80 个项目并且一次应该只显示大约 10 个。
在这里您可以看到它在从上到下滚动几次后如何快速填满堆:
这是我的适配器的外观:
class CustomerAdapter(
private val viewLifecycleOwner: LifecycleOwner,
private val listener: CustomerClickListener,
private val context: Context,
private val prefsShared: PrefsShared,
private val imageManager: ImageManager,
) : ListAdapter<Customer..., RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
...
CustomerViewHolderPhoto(
context, <-------------|
viewLifecycleOwner, <--|--- Are these actually the problem?
listener, <------------|
imageManager,
binding
)
}
}
class CustomerViewHolderPhoto(
private val context: Context,
private val viewLifecycleOwner: LifecycleOwner,
private val listener: CustomerAdapter.CustomerClickListener,
private val imageManager: ImageManager,
private val binding: ViewHolderCustomerItemPhotoBinding
) : RecyclerView.ViewHolder(binding.root), Releasable {
fun bind(contact: EntityContactWithCustomerWithPhoneNumbersAndVisits) {
...
itemView.setOnClickListener {
listener.onCustomerClicked(contact)
}
imageManager.loadContactImageIntoImageViewAsync(
lifecycleOwner = viewLifecycleOwner,
imageView = binding.imageViewCustomer,
lookupKey = contact.contact.contactBookLookupKey,
)
}
}
class CustomerFragment : Fragment(), CustomerAdapter.CustomerClickListener {
...
private fun setupCustomerAdapter(): CustomerAdapter {
val viewManager = LinearLayoutManager(activity)
val viewAdapter = CustomerAdapter(
viewLifecycleOwner = viewLifecycleOwner,
listener = this,
context = requireContext(),
prefsShared = prefsShared,
imageManager = imageManager
)
binding.recyclerView.apply {
layoutManager = viewManager
adapter = viewAdapter
}
return viewAdapter
}
}
【问题讨论】:
-
你有任何内存泄漏我会从那里开始吗?如果不是,那么我猜
ImageManager比Fragment生命周期更长,并且使用某种 lru 缓存并保留对Bitmap的引用 -
在我添加Lru缓存之前内存泄漏已经存在,imageManager是一个单例,它封装了联系人图像的加载。
-
你怎么知道这些是内存泄漏,只是内存中的无效引用?仅当无法从较高范围的对象中清除较低范围的对象时,才会发生内存泄漏。当您退出应用程序并重新启动而进程没有终止时会发生什么 - 您是否仍然有重复的引用,或者只是单例中 lru 缓存中的引用? Glide 有如下选项:
diskCacheStrategy(DiskCacheStrategy.NONE)skipMemoryCache(true)跳过缓存
标签: android kotlin android-recyclerview memory-leaks out-of-memory