androidkotlinmutablelivedata

How to use Single Live Event to show toast in Kotlin


I want to use single live event class to show toast (like flag) Here is my code what I tried. I want to not use peding like flag. how do I fix it?

MainViewModel

class MainViewModel(private val movieRepository: MovieRepository) : ViewModel() {
    val keyword = MutableLiveData<String>()
    val movieList = MutableLiveData<List<Movie>>()
    val msg = MutableLiveData<String>()
    val pending: AtomicBoolean = AtomicBoolean(false)

    fun findMovie() {
        val keywordValue = keyword.value ?: return
        pending.set(true)
        if (keywordValue.isNullOrBlank()) {
            msg.value = "emptyKeyword"
            return
        }
        movieRepository.getMovieData(keyword = keywordValue, 30,
            onSuccess = {
                if (it.items!!.isEmpty()) {
                    msg.value = "emptyResult"
                } else {
                    msg.value = "success"
                    movieList.value = it.items
                }
            },
            onFailure = {
                msg.value = "fail"
            }
        )
    }
}

MainActivity

 private fun viewModelCallback() {
        mainViewModel.msg.observe(this, {
            if (mainViewModel.pending.compareAndSet(true, false)) {
                when (it) {
                    "success" -> toast(R.string.network_success)
                    "emptyKeyword" -> toast(R.string.keyword_empty)
                    "fail" -> toast(R.string.network_error)
                    "emptyResult" -> toast(R.string.keyword_result_empty)
                }
            }
        })
}

Solution

  • Solution

    Step 1. Copy the SingleLiveEvent.kt to your app

    /*
     *  Copyright 2017 Google Inc.
     *
     *  Licensed under the Apache License, Version 2.0 (the "License");
     *  you may not use this file except in compliance with the License.
     *  You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     *  Unless required by applicable law or agreed to in writing, software
     *  distributed under the License is distributed on an "AS IS" BASIS,
     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *  See the License for the specific language governing permissions and
     *  limitations under the License.
     */
    
    package com.example.myapp;
    
    import android.util.Log;
    
    import androidx.annotation.MainThread;
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.lifecycle.LifecycleOwner;
    import androidx.lifecycle.MutableLiveData;
    import androidx.lifecycle.Observer;
    
    import java.util.concurrent.atomic.AtomicBoolean;
    
    /**
     * A lifecycle-aware observable that sends only new updates after subscription, used for events like
     * navigation and Snackbar messages.
     * <p>
     * This avoids a common problem with events: on configuration change (like rotation) 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 call().
     * <p>
     * Note that only one observer is going to be notified of changes.
     */
    public class SingleLiveEvent<T> extends MutableLiveData<T> {
    
        private static final String TAG = "SingleLiveEvent";
    
        private final AtomicBoolean mPending = new AtomicBoolean(false);
    
        @MainThread
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super 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, 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);
        }
    }
    

    Step 2. Use from your code.

    MainViewModel

    class MainViewModel(private val movieRepository: MovieRepository) : ViewModel() {
        val keyword = MutableLiveData<String>()
        val movieList = MutableLiveData<List<Movie>>()
        val msg = SingleLiveEvent<String>()
    
        fun findMovie() {
            val keywordValue = keyword.value ?: return
            if (keywordValue.isNullOrBlank()) {
                msg.value = "emptyKeyword"
                return
            }
            movieRepository.getMovieData(keyword = keywordValue, 30,
                onSuccess = {
                    if (it.items!!.isEmpty()) {
                        msg.value = "emptyResult"
                    } else {
                        msg.value = "success"
                        movieList.value = it.items
                    }
                },
                onFailure = {
                    msg.value = "fail"
                }
            )
        }
    }
    

    MainActivity

    private fun viewModelCallback() {
        mainViewModel.msg.observe(this, {
            when (it) {
                "success" -> toast(R.string.network_success)
                "emptyKeyword" -> toast(R.string.keyword_empty)
                "fail" -> toast(R.string.network_error)
                "emptyResult" -> toast(R.string.keyword_result_empty)
            }
        })
    }