Android 数据未更改时调用 ViewModel onChanged
Android ViewModel onChanged called when data isn't changed
我有一个 Fragment
,带有动态数量的自定义视图,由 EditText
和 Button
组成。我所做的是,每次用户在 EditText
中输入价格并单击 Button
时,我都会通过 ViewModel 发出 API 请求,而我的 Fragment
会观察到LiveData
在 ViewModel
.
到目前为止一切顺利,当我使用第一个自定义视图时。问题出现在第二个(和第三个)上,因为 onChanged()
方法显然被调用,即使数据没有改变,第二个和第三个自定义视图正在监听该数据,所以它们在他们不是触发数据更改的人(他们从第一个接收数据更改)。
当用户点击Button
时,我观察和获取价格的方式是这样的:
val observer = Observer<NetworkViewState> { networkViewState ->
processResponse(networkViewState, moneySpent, coin, date)
}
boardingHistoricalPriceViewModel.coinDayAveragePrice.observe(this, observer)
boardingHistoricalPriceViewModel.getDayAveragePrice(coin.symbol,
addedCoinDatePriceView.selectedSpinnerItem, dateInMillis)
发生的事情是当第二个自定义视图触发 API 请求时调用方法 processResponse
,但我收到的结果是 coinDayAveragePrice
之前的结果API 响应到达(这是来自第一个自定义视图的第一个 API 响应到达后的值)。
这是我的一部分 ViewModel
:
val coinDayAveragePrice: MutableLiveData<NetworkViewState> = MutableLiveData()
fun getDayAveragePrice(symbol: String, currency: String, dateInMillis: Long) {
coinRepository
.getDayAverage(symbol, currency, "MidHighLow", dateInMillis)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { coinDayAveragePrice.postValue(NetworkViewState.Loading()) }
.subscribeBy(onSuccess = {
coinDayAveragePrice.postValue(NetworkViewState.Success(it))
}, onError = { throwable ->
coinDayAveragePrice.postValue(NetworkViewState.Error(throwable.localizedMessage))
})
}
NetworkViewState
只是一个 sealed class
表示作为 API 请求响应的包装:
sealed class NetworkViewState {
class Loading : NetworkViewState()
class Success<out T>(val item: T) : NetworkViewState()
class Error(val errorMessage: String?) : NetworkViewState()
}
我也试过取消订阅或将 coinDayAveragePrice
设置为空,但我仍然遇到同样的问题。
提前致谢!
因此,在没有看到您的 ViewModel
的情况下,很难确定到底是什么问题,但我认为这就是我在评论中指出的问题。在这种情况下,一种解决方案是使用不同类型的 LiveData
。我从博客 post 中得到了这个基本想法(不记得 link :-/),但这是 class:
private const val TAG = "SingleLiveData"
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
*
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
*
* Note that only one observer is going to be notified of changes.
*/
open class SingleLiveData<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
if (hasActiveObservers()) {
Logger.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, wrapObserver(observer))
}
@MainThread
override fun observeForever(observer: Observer<T>) {
if (hasActiveObservers()) {
Logger.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
super.observeForever(wrapObserver(observer))
}
private fun wrapObserver(observer: Observer<T>): Observer<T> {
return Observer {
if (pending.compareAndSet(true, false)) {
observer.onChanged(it)
}
}
}
@MainThread
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
显然,这样做的一个问题是它不允许多个观察者观察同一个实时数据。但是,如果您需要,希望这篇 class 能给您一些想法。
我有一个 Fragment
,带有动态数量的自定义视图,由 EditText
和 Button
组成。我所做的是,每次用户在 EditText
中输入价格并单击 Button
时,我都会通过 ViewModel 发出 API 请求,而我的 Fragment
会观察到LiveData
在 ViewModel
.
到目前为止一切顺利,当我使用第一个自定义视图时。问题出现在第二个(和第三个)上,因为 onChanged()
方法显然被调用,即使数据没有改变,第二个和第三个自定义视图正在监听该数据,所以它们在他们不是触发数据更改的人(他们从第一个接收数据更改)。
当用户点击Button
时,我观察和获取价格的方式是这样的:
val observer = Observer<NetworkViewState> { networkViewState ->
processResponse(networkViewState, moneySpent, coin, date)
}
boardingHistoricalPriceViewModel.coinDayAveragePrice.observe(this, observer)
boardingHistoricalPriceViewModel.getDayAveragePrice(coin.symbol,
addedCoinDatePriceView.selectedSpinnerItem, dateInMillis)
发生的事情是当第二个自定义视图触发 API 请求时调用方法 processResponse
,但我收到的结果是 coinDayAveragePrice
之前的结果API 响应到达(这是来自第一个自定义视图的第一个 API 响应到达后的值)。
这是我的一部分 ViewModel
:
val coinDayAveragePrice: MutableLiveData<NetworkViewState> = MutableLiveData()
fun getDayAveragePrice(symbol: String, currency: String, dateInMillis: Long) {
coinRepository
.getDayAverage(symbol, currency, "MidHighLow", dateInMillis)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { coinDayAveragePrice.postValue(NetworkViewState.Loading()) }
.subscribeBy(onSuccess = {
coinDayAveragePrice.postValue(NetworkViewState.Success(it))
}, onError = { throwable ->
coinDayAveragePrice.postValue(NetworkViewState.Error(throwable.localizedMessage))
})
}
NetworkViewState
只是一个 sealed class
表示作为 API 请求响应的包装:
sealed class NetworkViewState {
class Loading : NetworkViewState()
class Success<out T>(val item: T) : NetworkViewState()
class Error(val errorMessage: String?) : NetworkViewState()
}
我也试过取消订阅或将 coinDayAveragePrice
设置为空,但我仍然遇到同样的问题。
提前致谢!
因此,在没有看到您的 ViewModel
的情况下,很难确定到底是什么问题,但我认为这就是我在评论中指出的问题。在这种情况下,一种解决方案是使用不同类型的 LiveData
。我从博客 post 中得到了这个基本想法(不记得 link :-/),但这是 class:
private const val TAG = "SingleLiveData"
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
*
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
*
* Note that only one observer is going to be notified of changes.
*/
open class SingleLiveData<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
if (hasActiveObservers()) {
Logger.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, wrapObserver(observer))
}
@MainThread
override fun observeForever(observer: Observer<T>) {
if (hasActiveObservers()) {
Logger.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
super.observeForever(wrapObserver(observer))
}
private fun wrapObserver(observer: Observer<T>): Observer<T> {
return Observer {
if (pending.compareAndSet(true, false)) {
observer.onChanged(it)
}
}
}
@MainThread
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
显然,这样做的一个问题是它不允许多个观察者观察同一个实时数据。但是,如果您需要,希望这篇 class 能给您一些想法。