Android Rx Java подписка срабатывает из ниоткуда - PullRequest
1 голос
/ 18 июня 2020

У меня есть простое приложение, которое отображает список контактов, извлеченный из 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

1 Ответ

1 голос
/ 25 июня 2020

После этих документов:

room / accessing-data # query-rx java

room-rx java -acb0cd4f3757

Flowable / Observable

Каждый раз, когда пользовательские данные обновляются, объект Flowable будет генерировать автоматически, что позволяет обновлять пользовательский интерфейс на основе последних данных.

В вашем коде после getContactByPages() вы вызываете saveContactsToDatabase() для сохранения данных в базе данных

Итак, viewModel.getContactFromDatabase() (который указывает на ContactDao.getContacts()) снова будет выдавать данные.

Если вы хотите, чтобы ContactDao.getContacts() излучал только один раз при вызове, подумайте о преобразовании ContactDao.getContacts() в MayBe / Single вместо Observable

Надеюсь, это поможет.

...