Android LiveData prevent receive the last value on observe

后端 未结 12 1264
自闭症患者
自闭症患者 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:29

    According to answer of jurij-pitulja.

    if we are using kotlin coroutines the solution look likes this.

    class Event<T>(private val content: T) {
    
        var isHandled = false
        private set
    
        fun getContentIfNotHandled(): T? {
            return takeIf { !isHandled }?.let {
                isHandled = true
                content
            }
        }
    }
    

    Inside of view model class replacing Flow.asLiveData() into emit new Event

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

    Implementing observer method inside of fragment

    viewModel.authResult.observe(viewLifecycleOwner){
                it.getContentIfNotHandled()?.run {
                    onAuthRequestComplete(this)
                }
            }
    
    0 讨论(0)
  • 2020-11-27 05:30

    More simple solution would be to use EventLiveData lib :

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

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

    You use it just like regular live data. It is extension of livedata and supports every feature of livedata. Unlike other solutions this supports multiple observers.

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

    0 讨论(0)
  • 2020-11-27 05:33

    Faced the same problem, and I created some simple kotlin extention functions which can solve the problem easily.

    Usage as below:

    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)
    

    Basic source code is explained as bllow.

    For some more detail (to support some special situations for example MediatorLiveData returned from Transformations.map), you can view it in 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()
        }
    }
    
    

    Becasue we need to access LiveData's pcakage visibile methond getVersion() for comparasion, so create a class in package android.arch.lifecycle or androidx.lifecycle (AndroidX):

    LiveDataHiddenApi.kt

    package androidx.lifecycle
    
    fun LiveData<*>.version(): Int {
        return this.getVersion()
    }
    
    0 讨论(0)
  • 2020-11-27 05:33

    I created a LiveData object FreshLiveData, which emits the onChange to the observer only after there is a call to setValue or 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)
                    }
                }
            }
    
        }
    }
    

    and here is an extension for transforming an existing LiveData to a 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
    }
    

    Usage:

    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
    
    0 讨论(0)
  • 2020-11-27 05:34

    I`m using this EventWraper class from Google Samples inside MutableLiveData

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

    In ViewModel :

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

    In Activity/Fragment

    mViewModel
        .onSaveEvent()
        .observe(
            getViewLifecycleOwner(),
            booleanEvent -> {
              if (booleanEvent != null)
                final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
                if (shouldSave != null && shouldSave) saveData();
              }
            });
    
    0 讨论(0)
  • 2020-11-27 05:34

    Just ignore data before android.arch.lifecycle.LiveData#observe function called.

    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)
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题