Android RxJava 订阅无处触发

Android RxJava subscribe triggered from nowhere

我有一个简单的应用程序,可以显示从 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() 的情况下被触发。
在没有网络的情况下打开应用程序(有本地数据库显示的联系人);
打开任何网络(wifi 或 4g);
尝试滑动刷新以从 API;
中提取联系人 getContacts()的订阅被触发(正常);
甚至没有调用函数 getContactsFromDatabase() 就触发了 viewModel.getContactFromDatabase() 的订阅 -- 问题

你可以在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

希望对您有所帮助。