仅对已更改的数据调用LiveData.observe()回调,并忽略控制器的生命周期更改(活动/片段)

时间:2019-09-27 09:48:24

标签: android android-livedata

我使用LiveData将错误消息发布到控制器(Activity / Fragment)。

设置了错误消息(MutableLiveData.setValue(...))后,回调(MutableLiveData.observe(lifecycleOwner, callback))会观察到错误消息的值,该回调在Snackbar UI组件中显示该消息。

Snackbar仅在短时间内可见,这很好。隐藏Snackbar之后,我想保持这种状态。

但是,在用户旋转显示之后,将重新创建控制器(Activity / Fragment)并再次通知观察者-因此,Snackbar再次出现,这不是我想要的

问题: 我以某种方式通过if(!isActivityRecreated()) { showSnackbar(...); }修复了该行为,但我想知道是否有一种方法可以告诉MutableLiveData实例仅应在更改的数据上调用观察者,从而忽略控制器的生命周期更改吗? >

(编辑)

@nik的答案: https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150

继续讨论: https://proandroiddev.com/livedata-with-single-events-2395dea972a8

“ LiveEvent”库:这是作者在第一个链接https://github.com/hadilq/LiveEvent中建议的SingleLiveEvent的全面实现。与其他解决方案相比,此实现似乎非常健壮。支持多个订阅并且是线程安全的。

2 个答案:

答案 0 :(得分:0)

正如@Michal Vician在评论中提到的,我只是将答案弄清楚了。您应该像下面那样扩展MutableLiveData。

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

private static final String TAG = "SingleLiveEvent";

private final AtomicBoolean mPending = new AtomicBoolean(false);

@MainThread
public void observe(LifecycleOwner owner, final Observer<T> observer) {

    if (hasActiveObservers()) {
        Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
    }

    // Observe the internal MutableLiveData
    super.observe(owner, new Observer<T>() {
        @Override
        public void onChanged(@Nullable T t) {
            if (mPending.compareAndSet(true, false)) {
                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);
}

}

refer link is here

答案 1 :(得分:0)

您可以使用this文章中所述的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;
}

}

它重写了observe()方法,从而绕过本地LiveData事件调度机制将观察者保存到内部地图中。 当您重新观察LiveData时,它将不会再次触发onChange。