Android 数据未更改时调用 ViewModel onChanged

Android ViewModel onChanged called when data isn't changed

我有一个 Fragment,带有动态数量的自定义视图,由 EditTextButton 组成。我所做的是,每次用户在 EditText 中输入价格并单击 Button 时,我都会通过 ViewModel 发出 API 请求,而我的 Fragment 会观察到LiveDataViewModel.

到目前为止一切顺利,当我使用第一个自定义视图时。问题出现在第二个(和第三个)上,因为 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 能给您一些想法。