У меня есть простое приложение, которое отображает список контактов, извлеченный из API.
Мне нужно отобразить последнюю выборку контактов, если при запуске приложения нет сети. Вот почему я работаю с Room, чтобы сохранить контакты в базе данных.
Контакты правильно сохранены, они правильно извлекаются, когда это необходимо.
Но возникает странная проблема, когда я делаю следующее pattern:
- я извлекаю некоторые контакты из API (автоматически сохраняются в локальной базе данных)
- я убиваю приложение;
- я вырезаю всю сеть, чтобы вызвать извлечение из локальной базы данных;
- я запускаю приложение без какой-либо сети, контакты правильно отображаются из локальной базы данных;
- я открываю сеть для обработки fre sh вызова 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()
.
Открыть приложение без сети (контакты отображаются из локальной БД);
Открыть любую сеть (wifi или 4g);
Попробовать проведите refre sh, чтобы вытащить контакты из API;
Срабатывает подписка getContacts()
(нормально);
Подписка viewModel.getContactFromDatabase()
запускается даже без вызова функции getContactsFromDatabase()
- ПРОБЛЕМА
Вы можете проверить мой код на Github