我有一个简单的应用程序,显示从API提取的联系人列表。
如果应用启动时没有网络,则需要显示最新的联系人提取。因此,这就是为什么我与Room一起将联系人保存到数据库中的原因。
触点已正确保存,需要时可将其正确拉出。
但是,当我执行以下模式时,会出现一个奇怪的问题:
-我从API中提取了一些联系人(自动保存到本地数据库中)
-我杀了这个应用;
-我切断了所有网络以触发从本地数据库的拉取;
-我在没有任何网络的情况下启动了该应用程序,可以从本地数据库正确显示联系人;
-我打开网络来处理对API的新调用(清理数据库等)。
-在响应对API的调用之后,在getContacts
调用的订阅之后,getContactsFromDatabase的订阅被称为!!!
因此,在调试之后,我发现仅调用了预订,而不调用完整功能getContactsFromDatabase()
,因为没有触发我在srList.isRefreshing = true
上的断点。订阅部分中只有断点。
我还尝试在仅调用getContactsFromDatabase函数的部分设置一个断点。它从未触发过,但是订阅中的断点已触发。
您可以在Github上检查我的代码
ContactListFragment.kt:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dataStorage = DataStorage(requireContext())
initView()
initListeners()
}
private fun initView() {
val layoutManager = LinearLayoutManager(activity)
rvContact.layoutManager = layoutManager
rvContact.itemAnimator = DefaultItemAnimator()
adapter = ContactListAdapter(this::onContactClicked)
rvContact.adapter = adapter
getContacts()
}
private fun initListeners() {
srList.setOnRefreshListener {
viewModel.page = 1; viewModel.contacts.clear(); getContacts()
}
rvContact.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val manager = rvContact.layoutManager as LinearLayoutManager
val visibleItemCount: Int = manager.childCount
val totalItemCount: Int = manager.itemCount
val firstVisibleItemPosition: Int = manager.findFirstVisibleItemPosition()
if (!srList.isRefreshing) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0
&& totalItemCount >= ContactListViewModel.PAGE_SIZE
) {
getContacts()
}
}
}
})
}
private fun getContacts() {
srList.isRefreshing = true
disposable.add(viewModel.getContactByPages()
.doOnError { e ->
srList.isRefreshing = false
if (viewModel.launch){
Timber.e("get contacts database")//breakpoint not triggered
getContactsFromDatabase()
}
e.localizedMessage?.let {
Timber.e(it)
}
val message = when (e) {
is BadRequestException -> {
getString(R.string.common_error_bad_request)
}
is ServerErrorException -> {
getString(R.string.common_error_server_error)
}
is UnknownHostException -> {
getString(R.string.common_error_no_connection)
}
else -> {
getString(R.string.common_error_basic)
}
}
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
.subscribe({ result ->
srList.isRefreshing = false
viewModel.page++
if (dataStorage.getBoolean(IS_FROM_CACHE)){//need a variable to clean the database after a first successful fetch
dataStorage.putBoolean(IS_FROM_CACHE, false).subscribe()
viewModel.contacts.clear()
cleanContactListOfDatabase()
}
viewModel.contacts.addAll(result.contacts)
saveContactsToDatabase()
adapter.updateList(viewModel.contacts)
tvNumberOfResult.text = getString(
R.string.contact_list_fragment_number_of_result,
viewModel.contacts.size
)
}, Throwable::printStackTrace)
)
}
private fun getContactsFromDatabase() {
srList.isRefreshing = true//breakpoint not triggered here
disposable.add(viewModel.getContactFromDatabase()
.doOnError {
srList.isRefreshing = false
}
.subscribe({
srList.isRefreshing = false// breakpoint triggered here
viewModel.launch = false
viewModel.contacts.addAll(it)
adapter.updateList(viewModel.contacts)
tvNumberOfResult.text = getString(
R.string.contact_list_fragment_number_of_result,
viewModel.contacts.size
)
dataStorage.putBoolean(IS_FROM_CACHE, true).subscribe()
}, Throwable::printStackTrace)
)
}
private fun saveContactsToDatabase() {
disposable.add(viewModel.insertAllContactsToDataBase()
.doOnError {
Timber.e("Insert error")
}
.subscribe({
Timber.d("Contacts saved")
}, Throwable::printStackTrace)
)
}
private fun cleanContactListOfDatabase(){
disposable.add(viewModel.cleanContactList()
.doOnError {
Timber.e("clean table error")
}
.subscribe({
Timber.e("Table cleaned")
}, Throwable::printStackTrace)
)
}
要恢复该问题,无需调用
viewModel.getContactFromDatabase()
函数即可触发getContactsFromDatabase()
的subscription方法。打开没有任何网络的应用程序(本地数据库显示联系人);
打开任何网络(WiFi或4g);
尝试滑动刷新以从API中提取联系人;
触发
getContacts()
的订阅(正常);甚至不调用函数
viewModel.getContactFromDatabase()
即可触发getContactsFromDatabase()
的订阅-问题您可以在Github上检查我的代码
最佳答案
遵循这些文档:
room/accessing-data#query-rxjava
room-rxjava-acb0cd4f3757
Flowable/Observable
Every time the user data is updated, the Flowable object will emit automatically, allowing you to update the UI based on the latest data.
在代码中,在
getContactByPages()
之后,您调用saveContactsToDatabase()
将数据保存到数据库因此
viewModel.getContactFromDatabase()
(指向ContactDao.getContacts()
)将再次发出数据。如果您希望
ContactDao.getContacts()
在被调用时仅发出一次,请考虑将ContactDao.getContacts()
转换为MayBe
/Single
而不是Observable
希望这可以帮助。
关于android - Android RxJava订阅无处触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62452860/