Android LiveData会在订阅时阻止接收最后一个值

问题描述 投票:3回答:6

是否有可能在开始观察时阻止LiveData接收最后一个值?我正在考虑使用LiveData作为事件。

例如,显示消息,导航事件或对话框触发器等事件,类似于EventBus。

我找到了类似SingleLiveEvent的东西 - 但它只适用于1个观察者,而不适用于多个观察者。

android android-architecture-components android-livedata mutablelivedata
6个回答
1
投票

我创建了一个新类,它将保存我的真实数据和“特殊ID”:

class LiveDataItem {
    long mRealtimeNanos;
    YOUR_PREVIOUS_LIVEDATA_TYPE mData;
    LiveDataItem(YOUR_PREVIOUS_LIVEDATA_TYPE data, long realtimeNanos) {
        this.mRealtimeNanos = realtimeNanos;
        this.mData = data;
    }
}

然后我创建了一个新的“全局”变量:

final List<Long> mExcludedRealtimeNanos = new ArrayList<>;

此时,我选择通过新的自定义“postValue()”方法“设置/ postValue()”我的“LiveDataItem”类型而不是原始的“YOUR_PREVIOUS_LIVEDATA_TYPE”类型:

public void myPostValue(YOUR_PREVIOUS_LIVEDATA_TYPE data, boolean notifyWhenObserved) {
    long cRealtimeNanos = SystemClock.realtimeNanos();
    if (!notifyWhenObserved) mExcludedRealtimeNanos.add(cRealtimeNanos);
    ....postValue(new LiveDataItem(data, cRealtimeNanos));
}

然后我创建了一个普通的观察者,它将接收所有“Changed()”事件,并在其中我检查了“RealtimeNanos”:

public void onChanged(LiveDataItem myDataItem) {
    boolean cFound = false;
    for (Long cRealtimeNanos : mExcludedRealtimeNanos) {
        if (cRealtimeNanos == myDataItem.mRealtimeNanos) {
            cFound = true;
            break;
        }
    }
    //check if it was found --> NO: it means that I wish to get the notification
    if (!cFound) mMyOnChangedCallback(myDataItem.mData)
}

遗憾的是,“mMyOnChangedCallback()”方法是一个回调函数,只要原始的“onChanged()”事件被引发就会被调用但是只有在设置为在数据创建期间通知它时才会调用。

您可以选择仅通过从“mExcludedRealtimeNanos”中删除THAT RealtimeNan来再次通知,然后将新的Observer附加到该LiveData。

几乎没有任何改变可以改进这段代码,但是我写下了我记得的旧代码(我现在离开了我的电脑)。例如,当使用我们的自定义postValue()方法发布新数据时,我们可以决定从“mExcludedRealtimeNanos”中删除一个值....


3
投票

我不认为可以防止LiveData在开始观察时接收最后一个值,如果你按原样使用它们。您可以做的是扩展ViewModel类,并仅在添加观察者时通知视图。

另一种选择是简单地忽略回调。

  1. 向ViewModel添加标志。 private boolean isFirstTime = true; public boolean getIsFirstTime() { return isFirstTime; } public boolean onObserverAdded() { isFirstTime = false; }`
  2. 在回调中添加检查 @Override public void onChanged(@Nullable final String newName) { boolean ignore = ((MyViewModel)ViewModelProviders.of(MyActivity.this).get(MyViewModel.class)).getIsFirstTime(); if(ignore) return; // Update the UI }
  3. 最后在添加观察者后调用onObserverAdded()

3
投票

有了一些RxJava的经验,我已经习惯于认为这种行为要求通常是Observeable(在我们的案例中是LiveData)的关注点。有许多operators,例如replay(),它可以控制实际发射(和何时)与用户发布的实际发布相比。从本质上讲,SingleLiveEvent也有相同的概念。

因此,我提出了MutableLiveData的这个改进的实现,称为VolatileLiveData

open class VolatileLiveData<T> : MutableLiveData<T>() {
    private val lastValueSeq = AtomicInteger(0)
    private val wrappers = HashMap<Observer<in T>, Observer<T>>()

    @MainThread
    public override fun setValue(value: T) {
        lastValueSeq.incrementAndGet()
        super.setValue(value)
    }

    @MainThread
    public override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        val observerWrapper = ObserverWrapper(lastValueSeq, observer)
        wrappers[observer] = observerWrapper
        super.observe(owner, observerWrapper)
    }

    @MainThread
    public override fun observeForever(observer: Observer<in T>) {
        val observerWrapper = ObserverWrapper(lastValueSeq, observer)
        wrappers[observer] = observerWrapper
        super.observeForever(observerWrapper)
    }

    @MainThread
    public override fun removeObserver(observer: Observer<in T>) {
        val observerWrapper = wrappers[observer]
        observerWrapper?.let {
            wrappers.remove(observerWrapper)
            super.removeObserver(observerWrapper)
        }
    }
}

private class ObserverWrapper<T>(private var currentSeq: AtomicInteger, private val observer: Observer<in T>) : Observer<T> {
    private val initialSeq = currentSeq.get()
    private var _observer: Observer<in T> = Observer {
        if (currentSeq.get() != initialSeq) {
            // Optimization: this wrapper implementation is only needed in the beginning.
            // Once a valid call is made (i.e. with a different concurrent sequence), we
            // get rid of it any apply the real implementation as a direct callthrough.
            _observer = observer
            _observer.onChanged(it)
        }
    }

    override fun onChanged(value: T) {
        _observer.onChanged(value)
    }
}

首先,类似于@emandt,我将唯一序列与每个实时值相关联 - 但严格地说在实时数据本身的范围内。只要将值设置为实时数据,就会设置此序列。

其次,受SingleLiveData的启发,我引入了围绕用户观察者的包装器,如果序列不同(即自订阅以来已经设置了新值),则只调用它。

这基本上总结了,但是对于完整的文档,请转到我的gist

Usage

至于使用它 - 如果你完全控制LiveData,只需使用VolatileLiveData,就像使用MutableLiveData一样。如果数据最初来自其他地方(例如房间),则可以使用Transformations.switchMap()以便“切换”到易失性实现。


2
投票

面对同样的问题,我创建了一些简单的kotlin扩展函数,可以轻松解决问题。

用法如下:

val liveData = MutableLiveData<String>()
liveData.value = "Hello"

val freshResult = mutableListOf<String>()
val normalResult = mutableListOf<String>()

liveData.observeForeverFreshly(Observer {
    freshResult.add(it)
})

liveData.observeForever(Observer {
    normalResult.add(it)
})

liveData.value = "World"

assertEquals(listOf("World"), freshResult)
assertEquals(listOf("Hello", "World"), normalResult)

基本源代码解释为bllow。

有关更多细节(以支持某些特殊情况,例如从Transformations.map返回的MediatorLiveData),您可以在github中查看它:livedata-ext

FreshLiveData.kt

fun <T> LiveData<T>.observeFreshly(owner: LifecycleOwner, observer: Observer<in T>) { 
    // extention fuction to get LiveData's version, will explain in below.
    val sinceVersion = this.version()
    this.observe(owner, FreshObserver<T>(observer, this, sinceVersion))
}

fun <T> LiveData<T>.observeForeverFreshly(observer: Observer<in T>, skipPendingValue: Boolean = true) {
    val sinceVersion = this.version()
    this.observeForever(FreshObserver<T>(observer, this, sinceVersion))
}

// Removes the observer which has been previously observed by [observeFreshly] or [observeForeverFreshly].
fun <T> LiveData<T>.removeObserverFreshly(observer: Observer<in T>) {
    this.removeObserver(FreshObserver<T>(observer, this, 0))
}

class FreshObserver<T>(
    private val delegate: Observer<in T>,
    private val liveData: LiveData<*>,
    private val sinceVersion: Int
) : Observer<T> {

    override fun onChanged(t: T) {
        if (liveData.version() > sinceVersion) {
            delegate.onChanged(t)
        }
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FreshObserver<*>
        if (delegate != other.delegate) return false
        return true
    }

    override fun hashCode(): Int {
        return delegate.hashCode()
    }
}

我们需要访问LiveData的pcakage visibile methond qazxsw poi进行比较,创建一个包qazxsw poi或getVersion()(AndroidX)的类:

LiveDataHiddenApi.kt

android.arch.lifecycle

2
投票

我在MutableLiveData中使用来自Google Samples的EventWraper类

androidx.lifecycle

在ViewModel中:

package androidx.lifecycle

fun LiveData<*>.version(): Int {
    return this.getVersion()
}

在活动/片段中

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
public class Event<T> {

    private T mContent;

    private boolean hasBeenHandled = false;


    public Event( T content) {
        if (content == null) {
            throw new IllegalArgumentException("null values in Event are not allowed.");
        }
        mContent = content;
    }

    @Nullable
    public T getContentIfNotHandled() {
        if (hasBeenHandled) {
            return null;
        } else {
            hasBeenHandled = true;
            return mContent;
        }
    }

    public boolean hasBeenHandled() {
        return hasBeenHandled;
    }
}

1
投票

即使我有同样的要求。我通过扩展MutableLiveData实现了这一点

 /** expose Save LiveData Event */
 public void newSaveEvent() {
    saveEvent.setValue(new Event<>(true));
 }

 private final MutableLiveData<Event<Boolean>> saveEvent = new MutableLiveData<>();

 LiveData<Event<Boolean>> onSaveEvent() {
    return saveEvent;
 }
© www.soinside.com 2019 - 2024. All rights reserved.