PagingDataAdapter 在片段被移除并重新添加后停止加载
PagingDataAdapter stops loading after fragment is removed and added back
我正在展示 Room ORM 在 PagingDataAdapter 上返回的 PagingSource
。
RecyclerView
出现在一个片段上 -- 我有两个这样的片段。切换时,它们会停止加载下一页的项目,滚动时只会显示占位符。
如果我不清楚我的意思,请查看这些屏幕截图--
- When I scroll without switching fragments, all the items are loaded
- When I switch Fragments before scrolling all the way down, the adapter stops loading new items
相关代码(请询问是否想看其他part/file)-
片段:
private lateinit var recyclerView: RecyclerView
private val recyclerAdapter = CustomersAdapter(this)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView = view.findViewById(R.id.recycler_view)
recyclerView.adapter = recyclerAdapter
recyclerView.layoutManager = LinearLayoutManager(context)
viewLifecycleOwner.lifecycleScope.launch {
viewModel.customersFlow.collectLatest { pagingData ->
recyclerAdapter.submitData(pagingData)
}
}
}
查看模型-
class CustomersListViewModel(application: Application, private val debtOnly: Boolean): ViewModel() {
private val db = AppDatabase.instance(application)
private val customersDao = db.customersDao()
val customersFlow = Pager(PagingConfig(20)) {
if (debtOnly)
customersDao.getAllDebt()
else
customersDao.getAll()
}.flow.cachedIn(viewModelScope)
}
在我检查了你的代码后,我发现了问题 FragmentTransaction.replace
函数和 flow.cachedIn(viewModelScope)
当activity调用replace
片段函数时,CustomerFragment会被销毁,它的ViewModel也会被销毁(触发viewModel.onCleared()
)所以这次cachedIn(viewModelScope)
也是无效的.
我有 3 个解决方案给你
解决方案 1:删除 .cachedIn(viewModelScope)
请注意,这只是临时解决方案,不推荐使用。
因此,activity 上仍然存在片段实例,但片段已被破坏(内存仍在泄漏)。
解决方案2:不使用Main activity中的FragmentTransaction.replace
函数,而是使用FragmentTransaction.add
函数:
不泄漏内存,仍然可以使用cachedIn
功能。当 activity 的片段很少并且片段的视图不太复杂时应该使用。
private fun switchNavigationFragment(navId: Int) {
when (navId) {
R.id.nav_customers -> {
switchFragment(allCustomersFragment, "Customer")
}
R.id.nav_debt -> {
switchFragment(debtCustomersFragment, "DebtCustomer")
}
}
}
private fun switchFragment(fragment: Fragment, tag: String) {
val existingFragment = supportFragmentManager.findFragmentByTag(tag)
supportFragmentManager.commit {
supportFragmentManager.fragments.forEach {
if (it.isVisible && it != fragment) {
hide(it)
}
}
if (existingFragment != fragment) {
add(R.id.fragment_container, fragment, tag)
.disallowAddToBackStack()
} else {
show(fragment)
}
}
}
解决方案 3:使用导航组件 Jetpack
这是最安全的解决方案。
它可以使用 Android Studio 的模板或以下一些文章来创建。
我尝试了解决方案 1 和 2,结果如下:
我正在展示 Room ORM 在 PagingDataAdapter 上返回的 PagingSource
。
RecyclerView
出现在一个片段上 -- 我有两个这样的片段。切换时,它们会停止加载下一页的项目,滚动时只会显示占位符。
如果我不清楚我的意思,请查看这些屏幕截图--
- When I scroll without switching fragments, all the items are loaded
- When I switch Fragments before scrolling all the way down, the adapter stops loading new items
相关代码(请询问是否想看其他part/file)-
片段:
private lateinit var recyclerView: RecyclerView
private val recyclerAdapter = CustomersAdapter(this)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView = view.findViewById(R.id.recycler_view)
recyclerView.adapter = recyclerAdapter
recyclerView.layoutManager = LinearLayoutManager(context)
viewLifecycleOwner.lifecycleScope.launch {
viewModel.customersFlow.collectLatest { pagingData ->
recyclerAdapter.submitData(pagingData)
}
}
}
查看模型-
class CustomersListViewModel(application: Application, private val debtOnly: Boolean): ViewModel() {
private val db = AppDatabase.instance(application)
private val customersDao = db.customersDao()
val customersFlow = Pager(PagingConfig(20)) {
if (debtOnly)
customersDao.getAllDebt()
else
customersDao.getAll()
}.flow.cachedIn(viewModelScope)
}
在我检查了你的代码后,我发现了问题 FragmentTransaction.replace
函数和 flow.cachedIn(viewModelScope)
当activity调用replace
片段函数时,CustomerFragment会被销毁,它的ViewModel也会被销毁(触发viewModel.onCleared()
)所以这次cachedIn(viewModelScope)
也是无效的.
我有 3 个解决方案给你
解决方案 1:删除 .cachedIn(viewModelScope)
请注意,这只是临时解决方案,不推荐使用。 因此,activity 上仍然存在片段实例,但片段已被破坏(内存仍在泄漏)。
解决方案2:不使用Main activity中的FragmentTransaction.replace
函数,而是使用FragmentTransaction.add
函数:
不泄漏内存,仍然可以使用cachedIn
功能。当 activity 的片段很少并且片段的视图不太复杂时应该使用。
private fun switchNavigationFragment(navId: Int) {
when (navId) {
R.id.nav_customers -> {
switchFragment(allCustomersFragment, "Customer")
}
R.id.nav_debt -> {
switchFragment(debtCustomersFragment, "DebtCustomer")
}
}
}
private fun switchFragment(fragment: Fragment, tag: String) {
val existingFragment = supportFragmentManager.findFragmentByTag(tag)
supportFragmentManager.commit {
supportFragmentManager.fragments.forEach {
if (it.isVisible && it != fragment) {
hide(it)
}
}
if (existingFragment != fragment) {
add(R.id.fragment_container, fragment, tag)
.disallowAddToBackStack()
} else {
show(fragment)
}
}
}
解决方案 3:使用导航组件 Jetpack
这是最安全的解决方案。 它可以使用 Android Studio 的模板或以下一些文章来创建。
我尝试了解决方案 1 和 2,结果如下: