0

I am doing my first internship and I was converting RX codes to LiveData in a project. At some point, I had to replace subscribe() functions with observeForever() + Globalscope(Dispatchers.Main) in some repositories but apparently using observeForever() is not the best thing to do and my internship mentor suggested me to use Transformations.map() instead .

I am not sure about how to use map() instead of observeForever in the following code (In a repository) :

        //I am using Globalscope with Dispathers.Main otherwise I get "cannot observeForever in a background Thread error
        GlobalScope.launch(Dispatchers.Main){
            someBooleanLiveData.observeForever { 
                if (it) {
                    // DO SOMETHING
                } else {
                    // DO SOMETHING ELSE
                }
            }
        }

What I understand from Transformations.map() function is that, it is used to map the value of a given LiveData object, just like the Map operator of ReactiveX

I tried this but doesn't seem to do what I want :

        Transformations.map(someBooleanLiveData){
            if (it) {
                // DO SOMETHING
            } else {
                // DO SOMETHING ELSE
            }
        }

My question is how should I use Transformation.map() to avoid observeForever ?

DO SOMETHING : may be a livedataObject.postValue(it) if you need an example

Thanks in advance for replies.

Oguzhan
  • 145
  • 1
  • 9
  • [Avoid GlobalScope](https://elizarov.medium.com/the-reason-to-avoid-globalscope-835337445abc) (from the lead developer of Kotlin) – Tenfour04 May 11 '21 at 14:12
  • This doesn't really answer my question ^^ but it seems really useful. I'll look at it when I am free (probably just after I find an answer for my question ) – Oguzhan May 11 '21 at 14:17
  • `Transformations.map()` is for creating a new LiveData that you will observe elsewhere. It's not really applicable to doing some action in response to what the values of the original LiveData were. If nothing is currently observing it, it won't do anything when the source LiveData value is changed. Is `someBooleanLiveData` being observed anywhere else in your project? Maybe LiveData isn't even appropriate for it. – Tenfour04 May 11 '21 at 14:37

1 Answers1

2

Transformations.map is intended to be used for transforming every element observed. So it doesn't have a conditional way to prevent an emission. By example:

Transformations.map(booleanLiveData) {
    if (it) 1 else 0
}

That returns a LiveData<Int>

If you want to add conditions for making your live data emit only sometimes then you have to use MediatorLiveData

class MyMediator : MediatorLiveData<SomeType>() {


    //we can create this wrapper method for convenience    
    fun addBooleanSource(booleanLiveData: LiveData<Boolean>) {
        //this method is already exposed and you can wrap it or do it in the invoker
        addSource(booleanLiveData) {
           if (it) {
                value = //do something
           }
        }
    }
     
}

The trick is the MediatorLiveData can access the value as a field so you can choose when to update it and when not to, also apply transformations in the process.

For using it the way you need to:

        private val _myMediator = MyMediator()
        val myMediator: LiveData<YourType>
            get() = _myMediator

        GlobalScope.launch(Dispatchers.Main){
            _myMediator.addBooleanSource(someBooleanLiveData)
        }

Then observe myMediator

cutiko
  • 9,887
  • 3
  • 45
  • 59
  • Yeah, I see now. I was so focused on using Transformations.map() to observe a LiveData object and I did not think about using a MediatorLiveData, not even for a second. I guess in terms of memory management (Thread usage etc.), MediatorLiveData is pretty same as Transformations.map(). I can probably do what I want using MediatorLiveData. Thanks – Oguzhan May 11 '21 at 14:49
  • @Oguzhan Both `MediatorLiveData` and `Transformations.map()` (which uses `MediatorLiveData` internally) will not give you the exact same behavior as your original code in the question, because they are passive until observed. Your code with `observeForever` will perform its action on every emission of the original LiveData. But there's not enough info about your specific case to know which makes more sense. – Tenfour04 May 11 '21 at 15:08
  • So, to accomplish what I am trying to do, I have to observe the MediatorLiveData or the LiveData returned by Transformations.map() somewhere in my code. Did I understand correctly ? – Oguzhan May 11 '21 at 15:13
  • @Oguzhan the problem is you are using a global scope there and for some reason observing forever, so is hard to guess why you did that. You are not supposed to do that. An observer is supposed to do that: be observed by others. Is not for listening something and updating things. Updated my answer with the best I can. Try to take a look at `Flow` and `Channels`. – cutiko May 11 '21 at 15:20
  • https://stackoverflow.com/questions/47515997/observing-livedata-from-viewmodel . I think I just reposted a question which was already asked and somehow an answer which is not an answer at all, is verified. I will delete to post and find something from comments. Thanks for answers – Oguzhan May 11 '21 at 15:25