ViewModel
とView
、Google architecture components
がLiveData
を使用してビューを変更にサブスクライブし、それに応じて自身を更新する適切な方法は何ですか。ただし、この通信は、メッセージの表示、進行状況の表示、進行状況の非表示など、単一のイベントには適していません。
Googleの例ではSingleLiveEvent
のようなハックがいくつかありますが、それは1人のオブザーバーに対してのみ機能します。一部の開発者はEventBus
を使用していますが、プロジェクトが大きくなるとすぐに制御不能になる可能性があると思います。
それを実装する便利で正しい方法はありますか?どのように実装しますか?
(Javaの例も歓迎)
進行状況ダイアログの表示/非表示、および画面のロード時に失敗したネットワーク呼び出しからのエラーメッセージを表示するには、ビューが監視しているLiveDataをカプセル化するラッパーを使用できます。
このメソッドの詳細は、アプリアーキテクチャの付録に記載されています。 https://developer.Android.com/jetpack/docs/guide#addendum
リソースを定義します。
_data class Resource<out T> constructor(
val state: ResourceState,
val data: T? = null,
val message: String? = null
)
_
そして、ResourceState:
_sealed class ResourceState {
object LOADING : ResourceState()
object SUCCESS : ResourceState()
object ERROR : ResourceState()
}
_
ViewModelで、リソースにラップされたモデルでLiveDataを定義します。
_val exampleLiveData = MutableLiveData<Resource<ExampleModel>>()
_
また、ViewModelで、現在の画面のデータをロードするためのAPI呼び出しを行うメソッドを定義します。
_fun loadDataForView() = compositeDisposable.add(
exampleUseCase.exampleApiCall()
.doOnSubscribe {
exampleLiveData.setLoading()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
exampleLiveData.setSuccess(it)
},
{
exampleLiveData.setError(it.message)
}
)
)
_
ビューで、作成時にオブザーバーを設定します。
_ viewModel.exampleLiveData.observe(this, Observer {
updateResponse(it)
})
_
次に、updateResponse()
メソッドの例を示します。進行状況を表示/非表示にし、必要に応じてエラーを表示します。
_private fun updateResponse(resource: Resource<ExampleModel>?) {
resource?.let {
when (it.state) {
ResourceState.LOADING -> {
showProgress()
}
ResourceState.SUCCESS -> {
hideProgress()
// Use data to populate data on screen
// it.data will have the data of type ExampleModel
}
ResourceState.ERROR -> {
hideProgress()
// Show error message
// it.message will have the error message
}
}
}
}
_
これを試して:
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
そしてそれをLiveDataにラッパーします
class ListViewModel : ViewModel {
private val _navigateToDetails = MutableLiveData<Event<String>>()
val navigateToDetails : LiveData<Event<String>>
get() = _navigateToDetails
fun userClicksOnButton(itemId: String) {
_navigateToDetails.value = Event(itemId) // Trigger the event by setting a new Event as a new value
}
}
そして観察する
myViewModel.navigateToDetails.observe(this, Observer {
it.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled
startActivity(DetailsActivity...)
}
})
リンク参照: イベントラッパーを使用