1

Currently, I am trying to optimize my app performance by improving the usage of different Dispatchers and contexts. One question I stumbled upon is that If I launch a suspend function inside a coroutine with a IO Dispatcher, will every other function be executed in the same dispatcher as well?

Example

fun doSomething() {
    viewModelScope.launch(Dispatchers.IO) {
       getUserData(viewModelScope)
    }
}

fun getUserData(innerScope: CoroutineScope) {
    workerList.startUserDataWorker()
    observeUserData(innerScope) // suspend function, is this called inside the IO Dipatcher?
}

// Will this be called inside the IO Dispatcher?
private suspend fun observeUserData(innerScope: CoroutineScope) {
    observerWorkerStateAndPassData(workerList.userDataWorkInfo, USER_DATA_OUTPUT_OPTION).collect { status ->
        when(status) {
            is Status.Loading -> {
                _userDataState.postValue(Status.loading())
            }
            is Status.Success -> {
                 // Will getShippingAddressList() also be called on the IO Dispatcher?
                _userDataState.postValue(Status.success(getShippingAddressList()))
            }
            is Status.Failure -> {
                _userDataState.postValue(Status.failed(status.message.toString()))
            }
        }
    }
}

// Getting Address from the local room cache. Is this called on the IO Dispatcher?
private suspend fun getShippingAddressList(): List<UserDeliveryAddress> {
    val uncachedList = userAddressDao.getAllAddress(UserAddressCacheOrder.SHIPPING)
    return userAddressCacheMapper.mapFromEntityList(uncachedList)
}
Andrew
  • 4,264
  • 1
  • 21
  • 65
  • 1
    Yes, unless you change the dispatcher manually using `withContext()` – Alpha 1 Jul 12 '21 at 13:41
  • Are there any disadvantages that everything is called on the IO Dispatcher? Is it possible to call "too many" suspend functions inside the IO Dispatcher and screw things up? – Andrew Jul 12 '21 at 13:43
  • No, But you should always switch to Dispatcher.Main for doing UI related things. – Alpha 1 Jul 12 '21 at 13:47

1 Answers1

3

Aside from the below exceptions, the dispatcher you're using is irrelevant when calling a suspend function. It is only relevant when calling blocking functions. Suspending doesn't use a dispatcher thread.

Exceptions:

  • Your suspend function is improperly designed and it actually blocks. This breaks convention for coroutines. A suspend function should never block.
  • Maybe your suspend function doesn't block, but it touches objects that are only allowed to be used on a specific thread, for example, most View classes on Android. There's no official convention for this situation--you could skip wrapping the contents of the function with withContext(Dispatchers.Main), and thereby pass along the main-only restriction up from the view class to your suspend function caller. IMO, since you can avoid the potential problem in the first place using withContext, you might as well, at least when the suspend function is public. You can use Dispatchers.Main.immediate to avoid an unnecessary thread switch.
  • Concurrency implications if you're working with objects across multiple simultaneous coroutines. For example, if you only touch a specific object using Main or a single-thread dispatcher, you don't have to worry about multiple threads touching it simultaneously. I would argue for proper encapsulation you should always wrap these usages of the object of concern using withContext(mySingleThreadDispatcher) so it will still be irrelevant which dispatcher is calling your suspend function.

In your example, it doesn't matter what dispatcher calls observeUserData because the function will suspend indefinitely while it collects. And when it collects, it only calls the non-blocking, thread-safe function LiveData.postValue().

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • So there is no difference between using `viewModelScope.launch(Dispatchers.IO)` and without the explicit dispatcher in terms of performance? Is there furthermore no difference, when I use `withContext(Dispatchers.IO) {}` in terms of performance? – Andrew Jul 12 '21 at 13:54
  • 1
    If you do blocking work on the Main dispatcher, you will get UI freezes. But otherwise, it's irrelevant to performance. It just affects which threads are being used to do the work. – Tenfour04 Jul 12 '21 at 13:57