LiveData 防止在开始观察时收到最后一个值

LiveData prevent receive the last value when start observing

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

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

关于ViewModel和fragment之间通信的问题,Google让我们LiveData用数据更新视图,但是当我们需要更新时这种通信方式不适合单个事件只查看一次,我们也不能在 ViewModel 中保存视图的引用并调用一些方法,因为它会造成内存泄漏。

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

---更新----

正如@EpicPandaForce 所说“没有理由将 LiveData 用作它不是 的东西”,问题的意图可能是 Communication between view and ViewModel in MVVM with LiveData

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

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

  1. 向 ViewModel 添加标志。

    private boolean isFirstTime = true;
    
    public boolean isFirstTime() { 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)).isFirstTime();
    if(ignore) return;
    
    // Update the UI
    }
    
  3. 添加观察者后最后调用onObserverAdded()

我创建了一个新的 Class 来保存我的真实数据和一个 "special ID":

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

然后我创建了一个新的 "global" 变量:

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

此时我选择 "set/postValue()" 我的 "LiveDataItem" 类型而不是原来的 "YOUR_PREVIOUS_LIVEDATA_TYPE" 通过新的自定义 "postValue()" 方法:

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

然后我创建了一个普通的 Observer,它将接收所有 "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" 中删除那个 RealtimeNanos,然后将一个新的观察者附加到那个 LiveData。

几乎没有什么改变可以改进这段代码,但我把我记得的旧代码写给你了(我现在不在电脑旁)。例如,当使用我们的自定义 postValue() 方法发布新数据时,我们可以决定从 "mExcludedRealtimeNanos" 中删除一个值....

有了一些使用 RxJava 的经验,我已经习惯于认为这种行为要求通常是 Observeable(在我们的例子中是 LiveData)的关注点。有许多 operators such as 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 的启发,我在用户的观察者周围引入了包装器,只有在序列不同时才调用它(即 new 值自订阅以来已设置)。

这基本上就是总结了,但要获得完整的文档,请转到我的 gist

用法

至于使用它 - 如果您可以完全控制 LiveData,只需像使用 MutableLiveData 一样使用 VolatileLiveData。如果数据最初来自其他地方(例如 Room),可以使用 Transformations.switchMap() 来对 volatile 实现进行 'switch'。

遇到同样的问题,我创建了一些简单的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)

基本源代码解释如下。

更多详细信息(支持一些特殊情况,例如 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
        if (delegate != (other as FreshObserver<*>).delegate) return false
        return true
    }

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

因为我们需要访问LiveData的pcakage visibile方法getVersion()进行比较,所以在包android.arch.lifecycleandroidx.lifecycle(AndroidX)中​​创建一个class:

LiveDataHiddenApi.kt

package androidx.lifecycle

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

我正在使用这个 EventWraper class 来自 MutableLiveDataGoogle 样本

/**
 * 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;
    }
}

在 ViewModel 中:

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

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

 public LiveData<Event<Boolean>> onSaveEvent() {
    return saveEvent;
 }

在Activity/Fragment

mViewModel
    .onSaveEvent()
    .observe(
        getViewLifecycleOwner(),
        booleanEvent -> {
          if (booleanEvent != null)
            final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
            if (shouldSave != null && shouldSave) saveData();
          }
        });

我也有同样的要求。我通过扩展 MutableLiveData

实现了这一点
package com.idroidz.android.ion.util;    
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Observer;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;

import java.util.concurrent.atomic.AtomicBoolean;

public class VolatileMutableLiveData<T> extends MutableLiveData<T> {


    private final AtomicBoolean mPending = new AtomicBoolean(false);

    @MainThread
    public void observe(LifecycleOwner owner, final Observer<T> observer) {
        // Observe the internal MutableLiveData
        mPending.set(false);
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.get()) {
                    observer.onChanged(t);
                }
            }
        });
    }

    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }

    public void callFromThread() {
        super.postValue(null);
    }
}

忽略调用 android.arch.lifecycle.LiveData#observe 函数之前的数据。

class IgnoreHistoryLiveData<T> : MutableLiveData<T>() {
    private val unactivedObservers = LinkedBlockingQueue<WrapperObserver<T>>()
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
        val wo = WrapperObserver<T>(observer)
        unactivedObservers.add(wo)
        super.observe(owner, wo)
    }


    override fun setValue(value: T) {
        while (unactivedObservers.isNotEmpty()) {
            unactivedObservers.poll()?.actived = true
        }
        super.setValue(value)
    }
}

private class WrapperObserver<T>(private val origin: Observer<T>) : Observer<T> {
    var actived = false
    override fun onChanged(t: T?) {
        if (actived) {
            origin.onChanged(t)
        }
    }
}

我创建了一个 LiveData 对象 FreshLiveData,它仅在调用 setValuepostValue.[=19= 后才向观察者发出 onChange ]

FreshLiveData.kt

/**
 * A lifecycle-aware observable that emits only new data after subscription. Any data that has
 * already been set, before the observable has subscribed, will be ignored.
 *
 * This avoids a common problem with events: on configuration change (like rotation, font change) 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 postValue().
 *
 * All observers will be notified of change(s).
 */
class FreshLiveData<T> : MutableLiveData<T>() {

    private val observers = mutableMapOf<LifecycleOwner, FreshLiveDataObserver>()

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        @Suppress("UNCHECKED_CAST")
        observer as Observer<T>
        observers[owner].apply {
            if (this == null) {
                observers[owner] = FreshLiveDataObserver(observer).apply {
                    super.observe(owner, this)
                }
            } else {
                add(observer)
            }
        }
    }

    override fun observeForever(observer: Observer<in T>) {
        @Suppress("UNCHECKED_CAST")
        observer as Observer<T>
        observers[ProcessLifecycleOwner.get()].apply {
            if (this == null) {
                observers[ProcessLifecycleOwner.get()] = FreshLiveDataObserver(observer).apply {
                    super.observeForever(this)
                }
            } else {
                add(observer)
            }
        }
    }

    override fun removeObservers(owner: LifecycleOwner) {
        observers.remove(owner)
        super.removeObservers(owner)
    }

    override fun removeObserver(observer: Observer<in T>) {
        @Suppress("UNCHECKED_CAST")
        observers.forEach { it.value.remove(observer as Observer<T>) }
        super.removeObserver(observer)
    }

    @MainThread
    override fun setValue(t: T?) {
        observers.forEach { it.value.setPending() }
        super.setValue(t)
    }

    override fun postValue(value: T) {
        observers.forEach { it.value.setPending() }
        super.postValue(value)
    }

    inner class FreshLiveDataObserver(observer: Observer<T>) : Observer<T> {
        private val observers = mutableSetOf<Observer<T>>()
        private val pending = AtomicBoolean(false)

        init {
            observers.add(observer)
        }

        fun add(observer: Observer<T>) = observers.add(observer)
        fun remove(observer: Observer<T>) = observers.remove(observer)
        fun setPending() = pending.set(true)

        override fun onChanged(t: T) {
            if (pending.compareAndSet(true, false)) {
                observers.forEach { observer ->
                    observer.onChanged(t)
                }
            }
        }

    }
}

这里是将现有 LiveData 转换为 FreshLiveData 的扩展。

LiveDataExtensions.kt

@MainThread
fun <T> LiveData<T>.toFreshLiveData(): LiveData<T> {
    val freshLiveData = FreshLiveData<T>()
    val output = MediatorLiveData<T>()
    // push any onChange from the LiveData to the FreshLiveData
    output.addSource(this) { liveDataValue -> freshLiveData.value = liveDataValue }
    // then push any onChange from the FreshLiveData out
    output.addSource(freshLiveData) { freshLiveDataValue -> output.value = freshLiveDataValue }
    return output
}

用法:

val liveData = MutableLiveData<Boolean>()
liveData.value = false
liveData.toFreshLiveData().observeForever {
    // won't get called with `it = false` because the observe was setup after setting that livedata value
    // will get called with `it = true` because the observer was setup before setting that livedata value
}
liveData.value = false

val freshLiveData = FreshLiveData<Boolean>()
freshLiveData.value = false
freshLiveData.observeForever {
    // won't get called with `it = false` because the observe was setup after setting that livedata value
    // will get called with `it = true` because the observer was setup before setting that livedata value
}
freshLiveData.value = true

没有理由将 LiveData 用作它不是的东西。如果你需要一个单独的行为(不保留以前的值的东西),那么你应该使用一个不保留以前的值的组件——而不是绕过它(“记住”它已经发出然后忘记发射等)

虽然没有其他可用的正确解决方案,所以我最终不得不自己写一个,因为没有其他选择可以推荐,所以我不得不推荐我为这个特定目的写的那个。

无论如何,你可以添加live-event库:

implementation 'com.github.Zhuinden:live-event:1.2.0'

来自 Jitpack:maven { url "https://jitpack.io" }

那你可以做

private val eventEmitter = EventEmitter<WordController.Events>()
val controllerEvents: EventSource<WordController.Events> = eventEmitter

controllerEvents.observe(viewLifecycleOwner) { event: WordController.Events ->
    when (event) {
        is WordController.Events.NewWordAdded -> showToast("Added ${event.word}")
    }.safe()
}

您可以使用 this 文章中描述的 EventLiveData。它将解决您的问题,我在 2 个生产项目中使用了它。 它是 LiveData 扩展,就像 SingleLiveData 但支持多个观察者。当观察者应该接收事件时,还允许自定义生命周期限制。例如,如果您不想在片段处于后台时接收事件。

EventLiveData 持有它永远观察的内部观察者,覆盖观察方法,绕过本机 LiveData 事件调度机制将观察者保存到内部映射中。

您可以copy/paste整个class或导入库,后者更方便

public  class EventLiveData<T> extends LiveData<T> {

private final HashMap<Observer<? super T>, EventObserverWrapper> observers= new HashMap<>();
private final Observer<T> internalObserver;
int mActiveCount = 0;

public EventLiveData() {
    this.internalObserver =  (new Observer<T>() {
        @Override
        public void onChanged(T t) {
            Iterator<Map.Entry<Observer<? super T>, EventObserverWrapper>> iterator = EventLiveData.this.observers.entrySet().iterator();
            while (iterator.hasNext()){
                EventObserverWrapper wrapper= iterator.next().getValue();
                if(wrapper.shouldBeActive())
                    wrapper.getObserver().onChanged(t);
            }
        }
    });
}
private void internalObserve(){
    super.observeForever(this.internalObserver);

}
@MainThread
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
    observe(owner, observer,STARTED,null);
}
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer, @NonNull Lifecycle.State minimumStateForSendingEvent) {
    observe(owner, observer,minimumStateForSendingEvent,null);
}
@MainThread
public void observeInOnStart(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
    observe(owner, observer,STARTED, Lifecycle.Event.ON_STOP);
}
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer, @NonNull Lifecycle.State minimumStateForSendingEvent, Lifecycle.Event removeObserverEvent) {
    assertMainThread("observe");
    assertNotNull(owner, "owner");
    assertNotNull(observer, "observer");
    assertNotNull(owner, "minimumStateForSendingEvent");
    assertDestroyedState(minimumStateForSendingEvent);
    assertMaximumEvent(removeObserverEvent);
    if(minimumStateForSendingEvent==DESTROYED){
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        StackTraceElement caller = stackTraceElements[3];
        String className = caller.getClassName();
        String methodName = caller.getMethodName();
        IllegalArgumentException exception =
                new IllegalArgumentException("State can not be equal to DESTROYED! : " +
                        "method " + className + "." + methodName +
                        ", parameter " + minimumStateForSendingEvent);
        throw sanitizeStackTrace(exception);
    }

    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        return;
    }

    EventLifecycleBoundEventObserver wrapper = new EventLifecycleBoundEventObserver(owner, observer);
    wrapper.setMinimumStateForSendingEvent(minimumStateForSendingEvent);
    wrapper.setMaximumEventForRemovingEvent(removeObserverEvent);
    EventObserverWrapper existing = wrapper;
    if(!observers.containsKey(observer))existing = observers.put(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper);

    if (!super.hasObservers()) {
        internalObserve();
    }

}
@MainThread
@Override
public void observeForever(@NonNull Observer observer) {
    assertMainThread("observeForever");
    assertNotNull(observer, "observer");
    EventAlwaysActiveEventObserver wrapper = new EventAlwaysActiveEventObserver(observer);
    EventObserverWrapper existing = wrapper;
    if(!observers.containsKey(observer))existing = observers.put(observer, wrapper);
    if (existing != null && existing instanceof EventLiveData.EventLifecycleBoundEventObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    if (!super.hasObservers()) {
        internalObserve();
    }
    wrapper.activeStateChanged(true);
}
/**
 {@inheritDoc}
 */
@Override
public void removeObservers(@NonNull  LifecycleOwner owner) {
    assertMainThread("removeObservers");
    assertNotNull(owner, "owner");
    Iterator<Map.Entry<Observer<? super T>, EventObserverWrapper>> iterator = EventLiveData.this.observers.entrySet().iterator();
    while (iterator.hasNext()){
        Map.Entry<Observer<? super T>, EventObserverWrapper> entry=iterator.next();
        if(entry.getValue() instanceof EventLiveData.EventLifecycleBoundEventObserver){
            EventLifecycleBoundEventObserver eventLifecycleBoundObserver =(EventLifecycleBoundEventObserver) entry.getValue();
            if(eventLifecycleBoundObserver.isAttachedTo(owner))this.observers.remove(entry.getKey());
        }
    }
}
@Override
public void removeObserver(@NonNull Observer observer) {
    assertMainThread("removeObserver");
    assertNotNull(observer, "observer");
    this.observers.remove(observer);

}
final protected void onActive() {}
protected void onActiveEvent() {}
protected void onInactive() {

}
@SuppressWarnings("WeakerAccess")
public boolean hasObservers() {
    return observers.size() > 0;
}
@SuppressWarnings("WeakerAccess")
public boolean hasActiveObservers() {
    return mActiveCount > 0;
}
class EventLifecycleBoundEventObserver extends EventObserverWrapper implements LifecycleObserver {
    @NonNull
    private final LifecycleOwner mOwner;
    private Lifecycle.State MINIMUM_STATE_FOR_SENDING_EVENT= STARTED;
    private Lifecycle.Event MAXIMUM_EVENT_FOR_REMOVING_EVENT= null;
    EventLifecycleBoundEventObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }

    public Lifecycle.State getMinimumStateForSendingEvent() {
        return MINIMUM_STATE_FOR_SENDING_EVENT;
    }

    public Lifecycle.Event getMaximumStateForRemovingEvent() {
        return MAXIMUM_EVENT_FOR_REMOVING_EVENT;
    }

    public void setMaximumEventForRemovingEvent(Lifecycle.Event MAXIMUM_EVENT_FOR_REMOVING_EVENT) {
        this.MAXIMUM_EVENT_FOR_REMOVING_EVENT = MAXIMUM_EVENT_FOR_REMOVING_EVENT;
    }

    public void setMinimumStateForSendingEvent(Lifecycle.State MINIMUM_STATE_FOR_SENDING_EVENT) {
        this.MINIMUM_STATE_FOR_SENDING_EVENT = MINIMUM_STATE_FOR_SENDING_EVENT;
    }

    @Override
    boolean shouldBeActive() {
        Lifecycle.State state=mOwner.getLifecycle().getCurrentState();
        return state.isAtLeast(MINIMUM_STATE_FOR_SENDING_EVENT);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED||(MAXIMUM_EVENT_FOR_REMOVING_EVENT!=null&&MAXIMUM_EVENT_FOR_REMOVING_EVENT==event)) {
            removeObserver(mObserver);
            return;
        }
        activeStateChanged(shouldBeActive());
    }
    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }
    @Override
    void detachObserver() {
        mOwner.getLifecycle().removeObserver(this);
    }
}

private abstract class EventObserverWrapper {
    protected final Observer<? super T> mObserver;
    boolean mActive;
    EventObserverWrapper(Observer<? super T> observer) {
        mObserver = observer;
    }
    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }
    void detachObserver() {
    }
    public Observer<? super T> getObserver() {
        return mObserver;
    }
    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        // immediately set active state, so we'd never dispatch anything to inactive
        // owner
        mActive = newActive;
        boolean wasInactive = EventLiveData.this.mActiveCount == 0;
        EventLiveData.this.mActiveCount += mActive ? 1 : -1;
        if (wasInactive && mActive) {
            onActiveEvent();
        }
        if (EventLiveData.this.mActiveCount == 0 && !mActive) {
            onInactive();
        }
    }
}

private class EventAlwaysActiveEventObserver extends EventObserverWrapper {

    EventAlwaysActiveEventObserver(Observer<? super T> observer) {
        super(observer);
    }
    @Override
    boolean shouldBeActive() {
        return true;
    }
}
private void assertDestroyedState(@NonNull Lifecycle.State minimumStateForSendingEvent){
    if(minimumStateForSendingEvent==DESTROYED){
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        StackTraceElement caller = stackTraceElements[3];
        String className = caller.getClassName();
        String methodName = caller.getMethodName();
        IllegalArgumentException exception =new IllegalArgumentException("State can not be equal to "+ minimumStateForSendingEvent +"method " + className + "." + methodName +", parameter   minimumStateForSendingEvent");
        throw sanitizeStackTrace(exception);}
}
private void assertMaximumEvent(@NonNull Lifecycle.Event maximumEventForRemovingEvent){
    if(maximumEventForRemovingEvent== Lifecycle.Event.ON_START||maximumEventForRemovingEvent== Lifecycle.Event.ON_CREATE
            ||maximumEventForRemovingEvent== Lifecycle.Event.ON_RESUME){
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        StackTraceElement caller = stackTraceElements[3];
        String className = caller.getClassName();
        String methodName = caller.getMethodName();
        IllegalArgumentException exception = new IllegalArgumentException("State can not be equal to "+maximumEventForRemovingEvent +  "method " + className + "." + methodName +", parameter  maximumEventForRemovingEvent" );
        throw sanitizeStackTrace(exception);
    }
}
private  void assertMainThread(String methodName) {
    boolean isUiThread = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? Looper.getMainLooper().isCurrentThread() : Thread.currentThread() == Looper.getMainLooper().getThread();
    if (!isUiThread) {throw new IllegalStateException("Cannot invoke " + methodName + " on a background"+ " thread"); }
}
private  void assertNotNull(Object value, String paramName) {
    if (value == null) {throwParameterIsNullException(paramName); } }
private  void throwParameterIsNullException(String paramName) {
    StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
    StackTraceElement caller = stackTraceElements[3];
    String className = caller.getClassName();
    String methodName = caller.getMethodName();
    IllegalArgumentException exception =
            new IllegalArgumentException("Parameter specified as non-null is null: " +
                    "method " + className + "." + methodName +
                    ", parameter " + paramName);
    throw sanitizeStackTrace(exception);
}
private   <T extends Throwable> T sanitizeStackTrace(T throwable) { return sanitizeStackTrace(throwable, this.getClass().getName());}
<T extends Throwable> T sanitizeStackTrace(T throwable, String classNameToDrop) {
    StackTraceElement[] stackTrace = throwable.getStackTrace();
    int size = stackTrace.length;
    int lastIntrinsic = -1;
    for (int i = 0; i < size; i++) {
        if (classNameToDrop.equals(stackTrace[i].getClassName())) {lastIntrinsic = i; } }
    StackTraceElement[] newStackTrace = Arrays.copyOfRange(stackTrace, lastIntrinsic + 1, size);
    throwable.setStackTrace(newStackTrace);
    return throwable;
}

}

更简单的解决方案是使用 EventLiveData 库:

implementation 'com.rugovit.eventlivedata:eventlivedata:1.0'

MutableEventLiveData<String>  eventLiveData =new MutableEventLiveData<>(); 
viewModel.event.observe(this, Observer {
    // ...
})
 

您可以像使用常规实时数据一样使用它。它是 livedata 的扩展,支持 livedata 的每一个特性。 与其他解决方案不同,它支持多个观察者。

Github link: https://github.com/rugovit/EventLiveData

根据 jurij-pitulja 的回答。

如果我们使用 kotlin coroutines 解决方案看起来像这样。

class Event<T>(private val content: T) {

    var isHandled = false
    private set

    fun getContentIfNotHandled(): T? {
        return takeIf { !isHandled }?.let {
            isHandled = true
            content
        }
    }
}

view model 内部 class 将 Flow.asLiveData() 替换为 emit new Event

val authResult: LiveData<Event<Result<AuthResponse>>> = _emailLiveData.switchMap { email ->
    liveData{
        repository.authRequest(email).collect{
            emit(Event(it))
        }
    }
}

fragment

中实现 observer 方法
viewModel.authResult.observe(viewLifecycleOwner){
            it.getContentIfNotHandled()?.run {
                onAuthRequestComplete(this)
            }
        }

我编写了一个小实用程序 class,它使我能够快速阻止任何代码块的第一个 运行,如下所示:

class SkipFirstRun {
    var canRun = false
    fun run(codeToRunButNotFirstTime: () -> Unit) {
        if (canRun) codeToRunButNotFirstTime()
        canRun = true
    }
}

然后在我的 Fragment 中声明它的一个实例变量:

class TrainingDetailCardInfoFragment 
    override val viewModel: TrainingViewModel by viewModels 
    private var skipInitialEndOfTraining = SkipFirstRun()
...

然后像这样使用它

    viewModel.runningTask.observe(viewLifecycleOwner) { task ->
            skipInitialEndOfTraining.run {
                Log.debug("Playing stopped!")
                sound.play(requireContext(), R.raw.endoftraining)
                playBarViewModel.stop()
            }
    }

我反复遇到这个问题,这似乎是新 recommended guidance on consuming single UI events 的 Flow 的问题。我难以理解的原因是您需要使用事件的方式存在细微差别。

著名的 EventWrapper 正在处理一个用例:

开始观察 -> 事件发生 -> re-establish观察并确保我们不不会再收到那个事件了

但是如果首先观察的行为也触发了最新数据怎么办?似乎 none 的官方指南解决了如果你 post 有意向 LiveData 发送某些内容,它将在第一次观察时传播 如果尚未观察到 ,即使您使用的是 Flow 或 EventWrapper。

有:

  • 您只想消费一次的事件 (SingleLiveEvent)
  • 您希望多个观察者只使用一次的事件(EventWrapper,或使用 StateFlow + 列表 + ID 的当前架构指南,带有大量疯狂的样板)
  • 并且只有在你开始观察一个或多个观察者之后你才想消费的事件(这个问题)

当视图模型在两个片段之间共享时,这尤其是一个问题。即片段 A post 是实时数据的一个值,然后点击几下或稍后网络响应片段 B 出现在屏幕上并开始观察相同的共享实时数据,您希望它丢弃最新结果并仅使用未来的结果. 在这种情况下,您将 LiveData 用于其设计目的之外的事情,并且需要查看条件以了解为什么片段 B 不应该关心最新结果并使用逻辑 在观察中过滤掉它。