2

I'm using a room database to run some heavy database operation. I am not using LiveData for this operation as I use the result for computation only. Now if in my main fragment I have this

override fun onActivityCreated(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        val result = viewModel.someHeavyOperation() // a suspend fun
        doSomething(result)
    }
}

I get a Skipped xx frames! Your application might be doing to much work on main thread. on startup which I don't get if I leave out the database query.

Now some answers here like this one or that one seem to suggest running the query on IO thread, like

override fun onActivityCreated(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        withContext(Dispatchers.IO) {
            val result = viewModel.someHeavyOperation() // a suspend fun
        }
        doSomething(result)
    }
}

which does increase performance for me. What confuses me though is that in an article by the android developers publication on medium it says

Note: Room uses its own dispatcher to run queries on a background thread. Your code should not use withContext(Dispatchers.IO) to call suspending room queries. It will complicate the code and make your queries run slower.

However, they only seem to consider the case where the expensive bits are the ensuing calculations, where they seem to suggest something like

override fun onActivityCreated(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        val result = viewModel.someOperation() // a suspend fun
        withContext(Dispatchers.Default) {
            doSomethingHeavy(result)
        }
    }
}

So now my questions:

  1. Why should it matter from which dispatcher room queries are called if room uses a custom dispatcher anyways?
  2. How to perform expensive room queries without blocking the main thread?
wave
  • 178
  • 2
  • 8

1 Answers1

2

Why should it matter from which dispatcher room queries are called if room uses a custom dispatcher anyways?

Room introduced Kotlin Coroutine support from version 2.1 onwards. Earlier, they were not supporting Coroutine. So, first of all, make sure if your room version is 2.1 or above from build.gradle file :

implementation "androidx.room:room-coroutines:${versions.room}"

If you're using Room version older than 2.1, then Room will perform operations on caller thread. This means that if we give query call of Room on MAIN thread, it'll execute operations on MAIN. If we give call to Room on IO - background thread, it'll execute on background.

How to perform expensive room queries without blocking the main thread?

For this, we should call Room queries on IO thread. You are already doing correct thing

override fun onActivityCreated(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
        withContext(Dispatchers.IO) {
            val result = viewModel.someHeavyOperation() // a suspend fun
        }
        doSomething(result)
    }
}

In addition to it, if you need to add waiting until Room query call returns, you can use async launcher and await() method

override fun onActivityCreated(savedInstanceState: Bundle?) {
    lifecycleScope.launch {
    val content = async(Dispatchers.IO) {
        viewModel.someHeavyOperation() // a suspend fun
    }
    // Using below line, we are introducing waiting for completion of someHeavyOperation() on IO thread
    // If we return any result from someHeavyOperation(), it can be accessed in result variable as below
    var result = content.await() 
    }
   }
}

Reference :

  1. https://stackoverflow.com/a/59376666/1994950
  2. https://stackoverflow.com/a/59408634/1994950
Kushal
  • 8,100
  • 9
  • 63
  • 82
  • So why does the [medium article](https://medium.com/androiddevelopers/coroutines-on-android-part-iii-real-work-2ba8a2ec2f45) say we shouldn't call room queries from IO thread? – wave Dec 31 '19 at 12:09
  • I cited it in my question: "Note: Room uses its own dispatcher to run queries on a background thread. Your code should not use withContext(Dispatchers.IO) to call suspending room queries. It will complicate the code and make your queries run slower." – wave Dec 31 '19 at 12:45
  • 1
    Okay, as I have mentioned, after Room version `2.1`, the recommendation is not to use `IO` thread from developer side as Room is doing it for us. But, if our legacy code is using Room version below `2.1`, developer need to handle it. – Kushal Dec 31 '19 at 13:09
  • It is recommendation, not rule. We can still use `IO` as good practise with `2.1` version of `Room` – Kushal Dec 31 '19 at 13:10
  • 1
    Thank you for clarifying that. I use version `2.2.3`, but my code runs faster with `Dispatchers.IO`. I assume it is because of complexities introduced in the ViewModel/Repository code ... – wave Dec 31 '19 at 13:22