我如何同步订阅一个可观察对象,以免错过该可观察对象的排放?

时间:2018-11-02 01:51:03

标签: android concurrency rx-java rx-java2 android-mvvm

我有一个具有MVVM架构的android应用。

视图层(一个片段)订阅了onStart()中ViewModel公开的可观察对象。我在该可观察对象上调用subscribe()之后,立即对ViewModel进行了直接调用,从而使事情开始了。通过这种直接调用,发生了两件事。首先,已订阅的可观察对象发出一个事件,以表示该应用程序处于加载状态。接下来,ViewModel获取一些数据,然后发出该数据。

问题是,我没有收到第一个排放物。但是,如果我将调用移到更远的生命周期链上(例如,在onCreate()中进行预订(而将调用保留在onStart()中),则确实收到了发射。显然,对subscribe()的调用是异步的,我如何确保在开始从其观察到对象之前可以订阅该对象?

在这种情况下,没有收到第一次发射。

//The fragment
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(OverviewFragmentViewModel::class.java)
    }

override fun onStart() {
    super.onStart()
    allSubscriptions.add(viewModel.uiStateChanged
        .subscribeOn(AndroidSchedulers.mainThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ uiState ->
            when (uiState) {
                is UiState.Loading -> showLoadingView()
                is UiState.ListReady -> showList(uiState)
                is UiState.Error -> showErrorView()
            }
        }, { error ->
            Log.e(TAG, error.message, error)
        })
    )
    viewModel.loadMovies()
}
}


//The ViewModel

class OverviewFragmentViewModel : ViewModel(){

    val uiStateChanged = PublishSubject.create<UiState>()
    val model = OverviewFragmentRepo()

    companion object {
        val TAG = OverviewFragmentViewModel::class.java.simpleName
    }

    override fun onCleared() {
        super.onCleared()
    }

    fun loadMovies(){
        //This is the emission that happens to fast for the fragment to receive it!
        uiStateChanged.onNext(UiState.Loading())
        model.getMovies()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({response ->
                uiStateChanged.onNext(UiState.ListReady(response.results))
            }, { error ->
                uiStateChanged.onNext(UiState.Error())
                Log.e(TAG, error.message, error)
            })
    }
}

现在,如果我只是向上移动订阅,就会收到发射。但是,我不想希望事情能及时完成,我想确定这一点,所以这就是为什么我希望能够保证在直接调用{{之前1}}。这是同一件事,订阅增加,接收到发射。

loadMovies()

1 个答案:

答案 0 :(得分:1)

从我的角度来看,您可以在ViewModel中创建可观察的对象,而不是创建PublishSubject和调用loadMovies(),例如:

val uiStateChanged = model.getMovies()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())    
    .compose(ResponseOrError.toResponseOrErrorObservable())
    .map { if (it.isData) UiState.ListReady(it.data().results) else UiState.Error() }
    .startWith(UiState.Loading())

然后您在Fragment中订阅了此Observable,然后可以删除viewModel.loadMovies()

read more about ResponseOrError