In Kotlin, using Coroutines, Lets say I have a job that starts on a click of a button, and shouldn't end until the activity's onStop
is called.
Something that looks like this:
button.setOnClickListener {
CoroutineScope(...).launch{
print("Button clicked")
// How to wait for "onStop()" ?
print("Activity stopped")
}
}
The above scenario is just an example of a general need to incorporate
asynchronous events that come from within an SDK in the form of a function invocation (onStop()
).
How should it be done? Thank you :]
So, whatever you want to execute when coroutine is done, you just put at the end of that coroutine and declare context (withContext) in which you want to execute it. The exception is of course if you start another async piece of code within coroutine (like another coroutine).
In order to migrate to the async/await pattern, you have to return the async() result from your code, and call await() on the Deferred , from within another coroutine. By doing so, you can remove callbacks you used to use, to consume asynchronously provided values.
Series - Kotlin coroutines Async/await is a common feature in many languages (naming might vary), that allows you to execute functions asynchronously while waiting for their results at a later point.
We can wait for the coroutine to finish by calling join() on the Job. For example, suppose we have a suspend function to download some files. We can launch this coroutine and capture the resulting job, which we can later use to join — to wait for the operation to complete.
I would create a special channel for a button, than send something to it from onStop(0)
and wait for it in your code:
val onStopChannel = Channel<Int>()
fun onStop() {
onStopChannel.offer(0)
}
button.setOnClickListener {
CoroutineScope(...).launch{
print("Button clicked")
onStopChannel.receive()
print("Activity stopped")
}
}
Any other observable could also work.
To address the question of:
a general need to incorporate asynchronous events that come from within an SDK in the form of a function invocation
I would like to add that the standard library's way of integrating asynchronous callbacks or futures from another library or API that doesn't use coroutines into your own code that does use coroutines is the suspendCoroutine
function. It suspends the execution of the current coroutine, takes a non-suspending lambda in which you register your callbacks/listeners, which when called will tell the coroutine via a Continuation
object to resume execution of the suspending function with a particular value or exception obtained from the callback.
For example, in the case of a network request:
suspend fun useValueFromApiCall(apiCall: Call<Thing>) {
val myThing: Thing = suspendCoroutine<Thing> { continuation ->
apiCall.enqueue(object: Callback<Thing> {
override fun onResponse(call: Call<Thing>, response: Response<Thing>) {
continuation.resume(response.body()) // assign response.body() to myThing
}
override fun onFailure(call: Call<Thing>, t: Throwable) {
continuation.resumeWithException(t) // throw t
}
})
}
// coroutine will suspend execution until the API call returns and
// either myThing contains the response or an exception was thrown
myThing.doSomething()
}
Here is a snippet of a talk explaining what's going on here quite nicely.
I would make my job wait with join()
function and then cancel it in onStop()
callback.
Something like:
class MainActivity : AppCompatActivity() {
private var job: Job = Job()
private val mainDispatchersContext = CoroutineScope(Dispatchers.Main)
private val coroutineContext = CoroutineScope(Dispatchers.Main + job)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainDispatchersContext.launch {
doJob()
}
}
private suspend fun doJob() {
coroutineContext.launch {
// do something
Log.v(TAG, "Job started")
job.join()
Log.v(TAG, "This line is not executed")
}
}
override fun onStop() {
super.onStop()
job.cancel()
Log.v(TAG, "Job end")
}
companion object{
const val TAG = "MainActivity"
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With