在我的Android应用中,我有一个非常基本的RecyclerView
实现,该实现通过使用Room和Paging library来显示数据库中的条目。
从数据库中获取条目的Dao方法:
@Query("SELECT * FROM entries")
abstract fun findAll(): DataSource.Factory<Int, Entry>
这就是从我的LiveData
中返回的DataSource.Factory
构造ViewModel
的方式:
private val config = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(512)
.setPrefetchDistance(256)
.setPageSize(256)
.build()
val entries = LivePagedListBuilder(entryService.findAll(), config).build()
// (entryService here just calls through to the Dao)
我只是观察LiveData
,并将更新的PagedList
传递给我的RecyclerView.Adapter
。
相应的RecyclerView.Adapter
代码段:
private val differ = AsyncPagedListDiffer<Entry>(this, DIFF_CALLBACK)
fun submitList(list: PagedList<Entry>) {
differ.submitList(list)
}
(我不使用PagedListAdapter
,因为稍后我需要在适配器中进行更细粒度的控制)
貌似一切正常,但是当我开始监视应用程序的内存使用情况时,我开始怀疑PagedList
项的处理不正确。
我在Callback
中添加了以下submitList()
:
list.addWeakCallback(differ.currentList?.snapshot(), object : PagedList.Callback() {
override fun onChanged(position: Int, count: Int) {
Log.d("_tag", "Position: $position | Changed: $count" +
" | Size: ${list.size}")
}
override fun onInserted(position: Int, count: Int) {
Log.d("_tag", "Position: $position | Inserted: $count" +
" | Size: ${list.size}")
}
override fun onRemoved(position: Int, count: Int) {
Log.d("_tag", "Position: $position | Removed: $count" +
" | Size: ${list.size}")
}
})
此后,我将5000个条目插入数据库,相应的日志条目如下:
D / _tag:位置:0 |插入:512 |大小:512
这很好,这是预期的输出。
但是当我开始向下滚动列表时,日志条目如下:
D / _tag:位置:512 |插入:256 |大小:768
D / _tag:位置:768 |插入:256 |大小:1024
D / _tag:位置:1024 |插入:256 |大小:1280
D / _tag:位置:1280 |插入:256 |大小:1536
然后日志到此为止,无论我在此点之后向下滚动多少,都不再有日志条目。
我对这种行为的疑问:
为什么从不调用onRemoved()
?
PagedList
是不断增长的列表,并且项目仍保留在内存中吗?
分页的目的不是按需加载新条目并删除旧条目(不可见)吗?
为什么即使我滚动到第1536个条目之后日志也停止了?
奇怪的是,即使一直滚动到列表的底部,条目也能正确显示。
如果有人能向我解释这种行为,我将非常感激。
(也欢迎Java回答)
编辑:
整个适配器的代码:
class EntryListAdapter
: RecyclerView.Adapter<EntryListAdapter.EntryViewHolder>() {
private val differ = AsyncPagedListDiffer<Entry>(this, DIFF_CALLBACK)
fun submitList(list: PagedList<Entry>) {
differ.submitList(list)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
EntryViewHolder {
val inflater = LayoutInflater.from(parent.context)
val entryItemView = inflater.inflate(R.layout.item_entry, parent, false)
return EntryViewHolder(entryItemView)
}
override fun onBindViewHolder(holder: EntryViewHolder, position: Int) {
differ.getItem(position)?.let { holder.bindTo(it) }
}
override fun getItemId(position: Int): Long {
return differ.getItem(position)?.id ?: RecyclerView.NO_ID
}
override fun getItemCount(): Int {
return differ.currentList?.size ?: 0
}
inner class EntryViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
fun bindTo(entry: Entry) {
itemView.textEntryTitle.text = entry.title
}
}
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Entry>() {
override fun areItemsTheSame(entry1: Entry, entry2: Entry): Boolean {
return entry1.id == entry2.id
}
override fun areContentsTheSame(entry1: Entry, entry2: Entry): Boolean
{
return entry1 == entry2
}
}
}
}
EDIT2 :
托管Fragment
的{{1}}的布局:
RecyclerView