1

我有一个Fragment, 具有动态数量的自定义视图,由一个EditText和一个组成Button。我所做的是,每次用户在 中键入价格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)

发生的事情是processResponse当第二个自定义视图触发 API 请求时调用该方法,但我收到的结果是coinDayAveragePriceAPI 响应到达之前的结果(这是来自第一个自定义视图的第一个 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,但我仍然遇到同样的问题。

提前非常感谢!

4

1 回答 1

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

显然,这样做的一个问题是它不允许同一实时数据的多个观察者。但是,如果你需要,希望这门课能给你一些想法。

于 2018-04-29T19:27:07.223 回答