https://kotlinlang.org logo
#coroutines
Title
# coroutines
g

gregd

08/27/2019, 8:49 PM
Hey guys! I’m struggling with
withContext
in production code (as opposite to hello world examples)… Where do you put
withContext
in your code? Is it inside suspending function body, or at the call site? Also, if you have nested suspending functions, let’s say a typical android app with ViewModel > Repository > DataSource, where DataSource contains suspending functions - where do you put
withContext
? Is there any official guidelines for this?
s

streetsofboston

08/27/2019, 8:51 PM
I tend to put it in the body of a
suspend
function, around the block of code that may be blocking (e.g. network access or something).
☝️ 1
o

octylFractal

08/27/2019, 8:51 PM
The general rule is you just put it wherever you need to change into another context. For example, before a blocking file read / network access, I do
withContext(<http://Dispatchers.IO|Dispatchers.IO>) { readFile() }
☝️ 1
Generally you do not use it when starting a new coroutine, as most coroutine builders accept a
context
parameter that is more efficient
s

streetsofboston

08/27/2019, 8:54 PM
Copy code
class DataStoreImpl(private val context: CoroutineContext, private val service: NetworkService) : DataStore {
    ...
    suspend override fun getData() : MyResult = withContext(context) {
        val nwData = service.getData() // blocking call
        val data : MyResult = ... ... nwData ...
        data
    }
    ...
}
And you’d provide
<http://Dispatchers.IO|Dispatchers.IO>
for the
context
property.
m

Marc Knaup

08/27/2019, 8:55 PM
You do it closer to the actual code that needs it. A repository for example may make network requests, file operations and heavy parsing. It would switch to IO for the former two and Default for the latter. Outside of the repository you can't (shouldn't) know what you need because of abstraction.
☝️ 1
s

streetsofboston

08/27/2019, 8:56 PM
^^^ Applying SOLID principles 🙂
g

gregd

08/27/2019, 8:57 PM
Ok, so if my DataSource has a suspend function that uses Firebase Storage (which is callback based), you think the best way of doing it would be like this?
Copy code
class DataSource {
  suspend fun getFile(...) = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
      suspendCoroutine {
          // firebase code...
      }
  }
}
o

octylFractal

08/27/2019, 8:58 PM
you don't need
<http://Dispatchers.IO|Dispatchers.IO>
if it's callback based
s

streetsofboston

08/27/2019, 8:58 PM
But you want to use coroutines to avoid callbacks 🙂
o

octylFractal

08/27/2019, 8:58 PM
yes, wrapping it with
suspendCoroutine
is the right call
s

streetsofboston

08/27/2019, 8:59 PM
Ah yes, of course… 🙂
g

gregd

08/27/2019, 8:59 PM
So is my whole snippet correct or not? ’cause I’m confused now ;D
s

streetsofboston

08/27/2019, 9:00 PM
Looks good! Does firebase use a callback?
g

gregd

08/27/2019, 9:00 PM
…Or shall I skip withContext in this case?
Yep, Firebase has this addOnSuccessListener, addOnFailureListener etc.
o

octylFractal

08/27/2019, 9:00 PM
drop the
withContext
-- it's not necessary in this case, because it's for blocking code. if you suspend, you don't need to be in that context
s

streetsofboston

08/27/2019, 9:01 PM
Using withContext, in your example, would be for blockig code. Yours doesn’t block, like Kenzie said.
When your callback is called by firebase, use the
Continuation
provided by
suspendCoroutine
to resume the
suspend fun getFile(...)
.
g

gregd

08/27/2019, 9:02 PM
Hmmm, you got me… I thought that this “waiting” for callback to be called also blocks the thread.
s

streetsofboston

08/27/2019, 9:02 PM
How does the firebase code look like?
g

gregd

08/27/2019, 9:03 PM
@streetsofboston yeah, I’m doing it
I can’t show you the whole code, but I’m calling resume & resumeWithException from firebase callbacks. And it works. I just thought I still need to set the proper context. But maybe you’re right guys 😉
s

streetsofboston

08/27/2019, 9:06 PM
Ah… good. No, that is not necessary. Sometimes, e.g. with some Bluetooth stuff, you must even call the callback registration functions on a particular thread (or on the same thread). Then you’d use
withContext
as well.
g

gregd

08/27/2019, 9:07 PM
My code looks something like this:
Copy code
class DataSource {
  suspend fun getFile(...) = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
      suspendCoroutine {
          firebase.getFile(...)
            .addOnSuccessListener { resume(it) }
            .addOnFailureListener { resumeWithException(it) }
      }
  }
}
But thanks to you I know I can skip withContext entirely 🙂
s

streetsofboston

08/27/2019, 9:08 PM
Yep;
firebase
doesn’t require its methods to be called on a particular thread, they can be called from any thread. You won’t need
withContext
.
g

gregd

08/27/2019, 9:09 PM
Ok, thanks a lot guys! I really appreciate it 👍
3 Views