Search code examples
javaandroidrx-java2

UI element checks its state before observes subscribes


I have a problem with updating refresh button state in my Android app. Inside my interactor I have a pair of subjects to control state of my refresh button.

class PageInteractor {
    private var isRefreshActionEnabled = false
    private val refreshStatePublisher by lazy {
        BehaviorSubject.create<Boolean>().apply {
            onNext(false)
        }
    }
    private var refreshActionPublisher: PublishSubject<Unit>? = null

    fun observeRefreshButtonState(): Observable<Boolean> {
        return refreshStatePublisher
    }

    fun observeRefreshAction(): Observable<Unit> {
        if (refreshActionPublisher?.hasComplete() == false && refreshActionPublisher?.hasObservers() == true) {
            refreshActionPublisher?.onComplete()
        }

        return PublishSubject.create<Unit>()
            .also {
                refreshActionPublisher = it
                updateRefreshButtonState()
            }
            .doOnDispose {
                updateRefreshButtonState()
            }
    }

    fun setRefreshActionEnabled(value: Boolean) {
        if (isRefreshActionEnabled != value) {
            isRefreshActionEnabled = value
            updateRefreshButtonState()
        }
    }

    fun onRefreshAction() {
        refreshActionPublisher?.onNext(Unit)
    }

    private fun updateRefreshButtonState() {
        refreshStatePublisher.onNext(refreshActionPublisher?.hasComplete() == false
            && refreshActionPublisher?.hasObservers() == true)
    }
}

I use this interactor in my presenter, it's a Moxy presenter.

class PagePresenter(
        private val pageInteractor: PageInteractor
) : MvpPresenter<PageView> {
    override fun onFirstViewAttach() {
        super.onFirstViewAttach()

        pageInteractor
            .observeRefreshButtonState()
            .subscribe { isEnabled ->
                viewState.setRefreshButtonEnabled(isEnabled)
            }
            .addTo(disposable)
    }

    fun onRefreshButtonClicked() {
        pageInteractor.onRefreshAction()
    }
}

My problem is that refresh button is disabled when I open the page. I did some debug and it seems that the source of the problem in method observeRefreshAction. This method creates PublishSubject, when sets refreshActionPublisher and right after that it calls method updateRefreshButtonState. This method checks if PublishSubject has any subscribers by calling hasObservers and result almost always false. It happens because PublishSubject is not yet returned to presenter, where I can subscribe on it.

Is there any ways to fix this problem? I have tried to move updateRefreshButtonState to doOnSubscribe, but it didn't help.


Solution

  • I tried to understand the problem and I wrote some piece of code and added some comments. So you don't need extra Publish Subject to invoke action, you can just call onRefreshAction() and change current state of refreshButtonState.

    import io.reactivex.Observable
    import io.reactivex.subjects.BehaviorSubject
    import kotlin.random.Random
    
    class PageInteractor {
    
        // Initial value is false and after subscription, subscriber will receive that value
        private val refreshButtonState = BehaviorSubject.createDefault(false)
    
        fun observeRefreshButtonState(): Observable<Boolean> = refreshButtonState.distinctUntilChanged()
    
        // Invoke refresh action
        fun onRefreshAction() {
            // Change state or toggle state or something else
            // Currently generate random next state
            val nextAction = Random.nextBoolean()
            refreshButtonState.onNext(nextAction)
        }
    }