首页 > 解决方案 > Are coroutines worth using with type mapping?

问题描述

Is it worth calling the coroutines in order to perform type mapping operations? Or it's going to be too much code bloating and/or overhead?

So the scenario is the following:


The Transformations.map code looks something like this

fun doSomethingWithRepoData(repoData: LiveData<List<A>>): LiveData<List<GUI_A>> =
    Transformations.map(repoData) { 
        it.map { GUI_A.fromRepoObject(it) }
    }

The function fromRepoObject only maps properties from the first object to the new instance of the second one.


The idea is to use coroutines in the map function to improve performance; but I don't know if it is going to be worth the hassle:

fun doSomethingWithRepoData(repoData: LiveData<List<A>>): LiveData<List<GUI_A>> =
    Transformations.map(repoData) { 
        it.map { async { GUI_A.fromRepoObject(it) } }
          .map { it.await() }
    }

标签: androidkotlinandroid-livedataandroid-jetpackkotlin-coroutines

解决方案


The definitive answer, and I hate it, is that it depends.

Switching thread's context doesn't come for free and it has some accumulated overhead on doing so you have to watch out for data's magnitude before considering one thing or another.

  • Mapping 15 elements? Probably not worth it.
  • Mapping 1000? Maybe! do they have nested lists and maps that should also be mapped?
  • Mapping 1_000_000? Definitely!

We should also watch out for our mapping implementations.

The given example:

fun doSomethingWithRepoData(repoData: LiveData<List<A>>): LiveData<List<GUI_A>> =
    Transformations.map(repoData) { 
        it.map { async { GUI_A.fromRepoObject(it) } }
          .map { it.await() }
    }

It's a don't do.

You see; LiveData assures that the code observing the LiveData is running on the main thread. On one hand that's good because it assures main safety but on the other hand it's "bad" in this case because the main thread waits for all of the corutines to finish their mapping operations before pushing a value.

A smarter approach to this, assuming we're in a ViewModel may be:

class SampleVM: ViewModel(){

    fun doSomethingWithRepoData(repoData: LiveData<List<A>>): LiveData<List<GUI_A>> = 
    Transformations.switchMap(repoData) { listA ->
        val result = MutableLiveData<List<GUI_A>>()
        // Don't block the UI thread by running in a coroutine
        viewModelScope.launch {
            listA
                 // Parallelize computation
                 .map { async(Dispatchers.Default) { GUI_A.fromRepoObject(it) }
                 // The viewModelScope job will await for all coroutines to finish mapping
                 .map { it.await() }
                 // Post the result to into the live data
                 .let { result.postValue(it) }
        }

        result
    }


}



推荐阅读