Android LiveData prevent receive the last value on observe

后端 未结 12 1324
自闭症患者
自闭症患者 2020-11-27 04:41

Is it possible to prevent LiveData receive the last value when start observing? I am considering to use LiveData as events.

For example eve

12条回答
  •  没有蜡笔的小新
    2020-11-27 05:20

    You can use EventLiveData described in this article. It is LiveData extension just like SingleLiveData but supports multiple observers. Also allows custom lifecycle limitation when observers should receive events. For example if you don't want to receive events when your fragments is in background.

    EventLiveData holds internal observer that it observes forever, overrides observe method saving observers into internal map bypassing native LiveData event dispatching mechanics.

    public  class EventLiveData extends LiveData {
    
    private final HashMap, EventObserverWrapper> observers= new HashMap<>();
    private final Observer internalObserver;
    int mActiveCount = 0;
    
    public EventLiveData() {
        this.internalObserver =  (new Observer() {
            @Override
            public void onChanged(T t) {
                Iterator, 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, EventObserverWrapper>> iterator = EventLiveData.this.observers.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry, 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 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 mObserver;
        boolean mActive;
        EventObserverWrapper(Observer observer) {
            mObserver = observer;
        }
        abstract boolean shouldBeActive();
    
        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }
        void detachObserver() {
        }
        public Observer 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 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 sanitizeStackTrace(T throwable) { return sanitizeStackTrace(throwable, this.getClass().getName());}
     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;
    }
    

    }

提交回复
热议问题