Android ViewModel onChanged在数据未更改时调用

时间:2018-04-29 17:01:36

标签: android kotlin android-architecture-components android-livedata android-viewmodel

我有一个Fragment,其中包含动态数量的自定义视图,包含EditTextButton。我所做的是,每次用户在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设置为null,但我仍遇到同样的问题。

提前多多感谢!

1 个答案:

答案 0 :(得分:0)

所以,如果没有看到你的ViewModel,很难确切地知道问题是什么,但我认为这是我在评论中指出的内容。在这种情况下,一种解决方案是使用不同类型的LiveData。我从博客文章中得到了这个基本想法(不记得链接: - /),但这里是班级:

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
    }
}

显然,这方面的一个问题是它不允许多个观察者使用相同的实时数据。但是,如果您需要,希望本课程能为您提供一些想法。