简体   繁体   English

Android RxJava 订阅无处触发

[英]Android RxJava subscribe triggered from nowhere

I have a simple app that displays contact list pulled from an API.我有一个简单的应用程序,它显示从 API 中提取的联系人列表。
I need to display the latest contacts fetch if there is no network at the launch of the app.如果应用程序启动时没有网络,我需要显示最新的联系人获取。 So that's why I'm working with Room to save the contacts into a database.这就是我与 Room 合作将联系人保存到数据库中的原因。

The contacts are correctly saved, they are correctly pulled when needed.联系人已正确保存,需要时正确拉动。
But there is a weird problem when I do the following pattern:但是当我执行以下模式时出现了一个奇怪的问题:
- I pull some contacts from the API (auto-saved into the local db) - 我从 API 中提取了一些联系人(自动保存到本地数据库中)
- I kill the app; - 我杀了应用程序;
- I cut all network to trigger the pull from the local db; - 我切断了所有网络以触发从本地数据库的拉取;
- I launch the app without any network, contacts are correctly displayed from the local db; - 我在没有任何网络的情况下启动应用程序,联系人从本地数据库正确显示;
- I open the network to process a fresh call to the API (clean the db and so on...) - 我打开网络来处理对 API 的新调用(清理数据库等等......)
- After the response to the call to the API, after the subscribe of the getContacts call, the subscribe of the getContactsFromDatabase is called !! - 对API的调用响应后, getContacts调用的subscribe后,getContactsFromDatabase的subscribe调用!!

So after debugging I found that just the subscribe is called and not the full function getContactsFromDatabase() because of my breakpoint on the srList.isRefreshing = true is not triggered.所以在调试之后我发现只调用了订阅而不是完整的 function getContactsFromDatabase()因为我在srList.isRefreshing = true上的断点没有被触发。 Only the breakpoint in the subscribe part.只有订阅部分的断点。

I also tried to put a breakpoint to the only part where the getContactsFromDatabase function is called.我还尝试在调用 getContactsFromDatabase function 的唯一部分设置断点。 It's never triggered however the breakpoint in the subscribe is triggered.它从未被触发,但是订阅中的断点被触发。

You can check my code on Github您可以在Github上查看我的代码

ContactListFragment.kt:联系人列表片段.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)
            )
        }

To resume the problem, the subscribe method of the viewModel.getContactFromDatabase() is triggered without the call of the function getContactsFromDatabase() .为了解决这个问题, viewModel.getContactFromDatabase()的 subscribe 方法在没有调用 function getContactsFromDatabase()的情况下被触发。
Open the app without any network (there are contacts displayed from the local db);在没有任何网络的情况下打开应用程序(有本地数据库显示的联系人);
Open any network (wifi or 4g);打开任何网络(wifi 或 4g);
Try a swipe refresh to pull contacts from the API;尝试滑动刷新以从 API 中提取联系人;
The subscribe of the getContacts() is triggered (normal); getContacts()的订阅被触发(正常);
The subscribe of the viewModel.getContactFromDatabase() is triggered without even the call of the function getContactsFromDatabase() -- PROBLEM viewModel.getContactFromDatabase()的订阅被触发,甚至没有调用 function getContactsFromDatabase() -- 问题

You can check my code on Github您可以在Github上查看我的代码

Following these docs:遵循这些文档:

room/accessing-data#query-rxjava 房间/访问数据#query-rxjava

room-rxjava-acb0cd4f3757 房间-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.每次更新用户数据时,Flowable object 会自动发出,让您根据最新数据更新 UI。

In your code, after getContactByPages() you call saveContactsToDatabase() to save the data to database在您的代码中,在getContactByPages()之后调用saveContactsToDatabase()将数据保存到数据库

So viewModel.getContactFromDatabase() (which is point to ContactDao.getContacts() ) will emit data again.因此viewModel.getContactFromDatabase() (指向ContactDao.getContacts() )将再次发出数据。

If you want ContactDao.getContacts() to emit only one time when is called, consider to convert ContactDao.getContacts() to MayBe / Single instead of Observable如果您希望ContactDao.getContacts()在被调用时仅发出一次,请考虑将ContactDao.getContacts()转换为MayBe / Single而不是Observable

Hope this helps.希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM