У меня есть приложение для Android с архитектурой MVVM.
Слой вида (фрагмент) подписывается на наблюдаемую, которая отображается с помощью ViewModel в onStart()
. Сразу после того, как я вызову 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)
})
}
}
Теперь, если я просто переместу подписку вверх, эмиссия будет получена. Однако я не хочу надеяться, что все завершится вовремя, я хочу быть в этом уверен, и поэтому я хочу быть в состоянии гарантировать, что я уже подписан, прежде чем делать прямой вызов loadMovies()
. Вот то же самое: подписка переместилась вверх, а эмиссия получена.
//The fragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(OverviewFragmentViewModel::class.java)
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)
})
)}
}
override fun onStart() {
super.onStart()
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)
})
}
}