48

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

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

ViewModel与fragment之间的通信有关的问题,谷歌让我们LiveData用数据更新视图,但是这种类型的通信不适合我们只需要用单个事件更新视图一次,而且我们不能保存视图的引用ViewModel并调用一些方法,因为它会造成内存泄漏。

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

- - 更新 - -

正如@EpicPandaForce所说“没有理由将LiveData用作它不是的东西”,问题的意图可能是MVVM中视图和ViewModel之间的通信与LiveData

4

13 回答 13

14

我在 MutableLiveData 中使用 Google Samples 中的这个 EventWraper 类

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

在视图模型中:

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

在活动/片段中

mViewModel
    .onSaveEvent()
    .observe(
        getViewLifecycleOwner(),
        booleanEvent -> {
          if (booleanEvent != null)
            final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
            if (shouldSave != null && shouldSave) saveData();
          }
        });
于 2019-03-17T23:00:43.963 回答
9

面对同样的问题,我创建了一些简单的 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。

更多细节(支持一些特殊情况,例如MediatorLiveData从 Transformations.map 返回),您可以在 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 可见方法getVersion()进行比较,所以在 package android.arch.lifecycleor androidx.lifecycle(AndroidX) 中创建一个类:

LiveDataHiddenApi.kt

package androidx.lifecycle

fun LiveData<*>.version(): Int {
    return this.getVersion()
}
于 2019-03-17T06:57:42.340 回答
7

有了一些 RxJava 的经验,我已经习惯于认为这种行为需求通常是ObserveableLiveData在我们的例子中)关注的问题。有许多操作符,例如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

用法

至于使用它 - 如果您可以完全控制它LiveData,只需VolatileLiveData按您的方式使用MutableLiveData。如果数据最初来自其他地方(例如 Room),Transformations.switchMap()则可以使用它来“切换”到volatile实现。

于 2019-02-11T20:36:45.580 回答
5

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

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

  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()在添加观察者后调用。

于 2018-10-06T17:18:41.187 回答
5

我创建了一个新类,它将保存我的真实数据和“特殊 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));
}

然后我创建了一个普通的 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”中删除一个值......

于 2018-10-09T15:13:56.150 回答
4

根据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替换Flow.asLiveData()emit new Event

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

内部实现observer方法fragment

viewModel.authResult.observe(viewLifecycleOwner){
            it.getContentIfNotHandled()?.run {
                onAuthRequestComplete(this)
            }
        }
于 2020-09-24T12:21:21.913 回答
2

没有理由将 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()
}
于 2020-01-14T10:48:14.067 回答
2

我创建了一个 LiveData 对象FreshLiveData,它onChange仅在调用setValueor后才向观察者发出postValue

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
于 2019-11-27T20:23:11.440 回答
1

即使我有同样的要求。我通过扩展 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);
    }
}
于 2019-04-24T14:03:27.550 回答
1

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

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

您可以复制/粘贴整个班级或导入库,这是更方便的方式

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

}

于 2020-01-14T22:48:16.007 回答
1

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

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

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

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

Github 链接:https ://github.com/rugovit/EventLiveData

于 2020-07-26T19:57:19.537 回答
0

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)
        }
    }
}
于 2019-11-07T09:47:22.443 回答
0

我编写了一个小型实用程序类,它可以快速阻止任何代码块的首次运行,如下所示:

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

然后在我的片段中,我只声明它的一个实例变量:

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()
            }
    }
于 2022-01-16T12:02:54.070 回答