Skip to content

Instantly share code, notes, and snippets.

@Audhil
Last active September 19, 2020 18:42
Show Gist options
  • Save Audhil/8a1b4cf98056ecec8f21e1b5d8ae07c2 to your computer and use it in GitHub Desktop.
Save Audhil/8a1b4cf98056ecec8f21e1b5d8ae07c2 to your computer and use it in GitHub Desktop.
Kotlin CoRoutines in NutShell!
package com.example.croutinesdemo
import retrofit2.Call
import retrofit2.Response
import retrofit2.http.GET
// api
interface API {
@GET("/comments")
fun getComments(): Call<List<Comment>> // apiCallWithRetrofitDemo1(), apiCallWithRetrofitDemo2()
// @GET("/comments")
// suspend fun getComments(): Response<List<Comment>> // apiCallWithRetrofitDemo1(), apiCallWithRetrofitDemo2()
}
package com.example.croutinesdemo
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*
import retrofit2.Retrofit
import retrofit2.await
import retrofit2.awaitResponse
import retrofit2.converter.gson.GsonConverterFactory
import kotlin.system.measureTimeMillis
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
println("yup hello from ${Thread.currentThread().name}")
// basicCoroutine1()
// suspendFunc2()
// contextSwitching3()
// runBlockingDemo4()
// jobJoinDemo5()
// jobCancelDemo5()
// jobCancel2Demo5()
// jobCancel3Demo5()
// jobCancel4Demo5()
// asyncAwait1Demo6()
// asyncAwait2Demo6()
// asyncAwait3Demo6()
// customScopes1Demo7()
// customScopes2Demo7()
// setUpRetroFit()
// apiCallWithRetrofitDemo1()
// apiCallWithRetrofitDemo2()
// apiCallWithRetrofitDemo3()
}
// private fun apiCallWithRetrofitDemo3() {
// GlobalScope.launch(Dispatchers.IO) {
// val response = apiService.getComments()
// if (response.isSuccessful) {
// response.body()?.forEach {
// println("yup: it is comment: $it")
// }
// }
// }
// }
private fun apiCallWithRetrofitDemo2() {
GlobalScope.launch(Dispatchers.IO) {
val response = apiService.getComments().awaitResponse()
if (response.isSuccessful) {
response.body()?.forEach {
println("yup: it is comment: $it")
}
}
}
}
private val BASE_URL = "https://jsonplaceholder.typicode.com"
private lateinit var apiService: API
private fun setUpRetroFit() {
apiService = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(API::class.java)
}
private fun apiCallWithRetrofitDemo1() {
GlobalScope.launch(Dispatchers.IO) {
val comments = apiService.getComments().await()
comments.forEach {
println("yup: it is comment: $it")
}
}
}
private fun customScopes2Demo7() {
// this is best practice - similarly
/*
* 2020-09-16 00:47:01.567 14972-14972/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-16 00:47:04.703 14972-14972/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:47:05.706 14972-14972/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:47:06.708 14972-14972/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:47:07.712 14972-14972/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:47:08.726 14972-14972/com.example.croutinesdemo I/System.out: yup: still running!
*/
tvDummy.setOnClickListener {
// bad practice
lifecycleScope.launch {
while (true) {
delay(1000L)
println("yup: still running!")
}
}
// launch another coroutine
GlobalScope.launch {
delay(5000L)
startActivity(Intent(this@MainActivity, SecondActivity::class.java))
finish()
}
}
}
private fun customScopes1Demo7() {
// it's bad practice - since we used global scope, instead of activity scope
/*
* 2020-09-16 00:43:55.669 14849-14849/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-16 00:43:59.872 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:00.875 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:01.877 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:02.881 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:03.884 14849-14904/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:04.893 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:05.895 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:06.897 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:07.900 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:08.903 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:09.907 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:10.910 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:11.912 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:12.915 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:13.929 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:14.932 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:15.934 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:16.936 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:17.939 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:18.944 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
2020-09-16 00:44:21.576 14849-14903/com.example.croutinesdemo I/System.out: yup: still running!
* */
tvDummy.setOnClickListener {
// bad practice
GlobalScope.launch {
while (true) {
delay(1000L)
println("yup: still running!")
}
}
// launch another coroutine
GlobalScope.launch {
delay(5000L)
startActivity(Intent(this@MainActivity, SecondActivity::class.java))
finish()
}
}
}
private fun asyncAwait3Demo6() {
// this is BEST practice
GlobalScope.launch(Dispatchers.IO) {
val time = measureTimeMillis {
val ans1: Deferred<String> = async { doNetworkCall1() }
val ans2: Deferred<String> = async { doNetworkCall2() }
println("yup: ans1: ${ans1.await()}")
println("yup: ans2: ${ans2.await()}")
}
println("yup it took: $time ms!") // 3 sec - which is desirable
}
}
private fun asyncAwait2Demo6() {
// this is BAD practice
GlobalScope.launch(Dispatchers.IO) {
val time = measureTimeMillis {
var ans1: String? = null
var ans2: String? = null
val job1 = launch { ans1 = doNetworkCall1() }
val job2 = launch { ans2 = doNetworkCall2() }
job1.join()
job2.join()
println("yup: ans1: $ans1")
println("yup: ans2: $ans2")
}
println("yup it took: $time ms!") // 3 secs - each request executes async but more boiler plate code
}
}
private fun asyncAwait1Demo6() {
// this is BAD practice
/*
* 2020-09-16 00:18:18.177 14066-14066/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-16 00:18:24.566 14066-14117/com.example.croutinesdemo I/System.out: yup: resp1: this is resp1
2020-09-16 00:18:24.567 14066-14117/com.example.croutinesdemo I/System.out: yup: resp2: this is resp2
2020-09-16 00:18:24.567 14066-14117/com.example.croutinesdemo I/System.out: yup it took: 6082 ms!
* */
// it'll take tot 6 secs to print result
GlobalScope.launch(Dispatchers.IO) {
val time = measureTimeMillis {
val resp1 = doNetworkCall1() // 3 sec
val resp2 = doNetworkCall2() // 3 sec
println("yup: resp1: $resp1")
println("yup: resp2: $resp2")
}
println("yup it took: $time ms!") // 6 secs - which is undesirable
}
}
private fun jobCancel4Demo5() {
/*
* with timeout
*
*
* 2020-09-15 23:28:18.585 13481-13481/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-15 23:28:18.844 13481-13535/com.example.croutinesdemo I/System.out: yup: coroutine started!
2020-09-15 23:28:20.721 13481-13535/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 30, res: 832040
2020-09-15 23:28:20.755 13481-13535/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 31, res: 1346269
2020-09-15 23:28:20.820 13481-13535/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 32, res: 2178309
2020-09-15 23:28:20.889 13481-13535/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 33, res: 3524578
2020-09-15 23:28:21.023 13481-13535/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 34, res: 5702887
2020-09-15 23:28:21.298 13481-13535/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 35, res: 9227465
2020-09-15 23:28:21.842 13481-13535/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 36, res: 14930352
2020-09-15 23:28:21.842 13481-13535/com.example.croutinesdemo I/System.out: yup: coroutine ended!
* */
GlobalScope.launch(Dispatchers.Default) {
println("yup: coroutine started!")
withTimeout(1000L) { // cancels automatically after 1sec
// long running work
for (i in 30..40) {
if (isActive)
println("yup: fib calculated, for i: $i, res: ${fib(i)}")
}
}
println("yup: coroutine ended!")
}
}
private fun jobCancel3Demo5() {
/*
*
* it's cancelling properly
* 2020-09-15 23:23:32.312 13222-13222/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-15 23:23:32.313 13222-13275/com.example.croutinesdemo I/System.out: yup: coroutine started!
2020-09-15 23:23:32.338 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 30, res: 832040
2020-09-15 23:23:32.365 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 31, res: 1346269
2020-09-15 23:23:32.412 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 32, res: 2178309
2020-09-15 23:23:32.467 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 33, res: 3524578
2020-09-15 23:23:32.573 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 34, res: 5702887
2020-09-15 23:23:32.774 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 35, res: 9227465
2020-09-15 23:23:33.017 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 36, res: 14930352
2020-09-15 23:23:33.314 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 37, res: 24157817
2020-09-15 23:23:33.314 13222-13222/com.example.croutinesdemo I/System.out: yup: cancel called!
2020-09-15 23:23:33.314 13222-13222/com.example.croutinesdemo I/System.out: yup: main thread continuing it's work!
2020-09-15 23:23:33.958 13222-13275/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 38, res: 39088169
2020-09-15 23:23:33.958 13222-13275/com.example.croutinesdemo I/System.out: yup: coroutine ended!
* */
val job = GlobalScope.launch(Dispatchers.Default) {
println("yup: coroutine started!")
// long running work
for (i in 30..40) {
if (isActive)
println("yup: fib calculated, for i: $i, res: ${fib(i)}")
}
println("yup: coroutine ended!")
}
runBlocking {
delay(1000L)
job.cancel()
println("yup: cancel called!")
println("yup: main thread continuing it's work!")
}
}
private fun jobCancel2Demo5() {
/*
it's not getting cancelled, because coroutine is busy doing the calculation within the loop
*
* 2020-09-15 23:20:42.064 13102-13102/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-15 23:20:42.162 13102-13161/com.example.croutinesdemo I/System.out: yup: coroutine started!
2020-09-15 23:20:42.182 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 30, res: 832040
2020-09-15 23:20:42.210 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 31, res: 1346269
2020-09-15 23:20:42.242 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 32, res: 2178309
2020-09-15 23:20:42.303 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 33, res: 3524578
2020-09-15 23:20:42.370 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 34, res: 5702887
2020-09-15 23:20:42.500 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 35, res: 9227465
2020-09-15 23:20:42.699 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 36, res: 14930352
2020-09-15 23:20:43.002 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 37, res: 24157817
2020-09-15 23:20:43.181 13102-13102/com.example.croutinesdemo I/System.out: yup: cancel called!
2020-09-15 23:20:43.181 13102-13102/com.example.croutinesdemo I/System.out: yup: main thread continuing it's work!
2020-09-15 23:20:43.515 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 38, res: 39088169
2020-09-15 23:20:44.304 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 39, res: 63245986
2020-09-15 23:20:45.563 13102-13161/com.example.croutinesdemo I/System.out: yup: fib calculated, for i: 40, res: 102334155
2020-09-15 23:20:45.563 13102-13161/com.example.croutinesdemo I/System.out: yup: coroutine ended!
*
* */
val job = GlobalScope.launch(Dispatchers.Default) {
println("yup: coroutine started!")
// long running work
for (i in 30..40) {
println("yup: fib calculated, for i: $i, res: ${fib(i)}")
}
println("yup: coroutine ended!")
}
runBlocking {
delay(1000L)
job.cancel()
println("yup: cancel called!")
println("yup: main thread continuing it's work!")
}
}
private fun fib(n: Int): Int {
return if (n == 0) 0
else if (n == 1) 1
else fib(n - 1) + fib(n - 2)
}
private fun jobCancelDemo5() {
/*
* 2020-09-15 23:14:21.792 12876-12876/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-15 23:14:21.888 12876-12911/com.example.croutinesdemo I/System.out: yup: this coroutine is running!
2020-09-15 23:14:22.902 12876-12911/com.example.croutinesdemo I/System.out: yup: this coroutine is running!
2020-09-15 23:14:23.905 12876-12876/com.example.croutinesdemo I/System.out: yup: job cancelled!
2020-09-15 23:14:23.905 12876-12876/com.example.croutinesdemo I/System.out: yup: main thread is continuing!
* */
val job = GlobalScope.launch(Dispatchers.IO) {
repeat(5) {
println("yup: this coroutine is running!")
delay(1000L)
}
}
runBlocking {
delay(2000L)
job.cancel()
println("yup: job cancelled!")
println("yup: main thread is continuing!")
}
}
private fun jobJoinDemo5() {
/*
* 2020-09-15 23:03:47.041 12085-12085/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-15 23:03:47.275 12085-12144/com.example.croutinesdemo I/System.out: yup: CoRoutine is running!
2020-09-15 23:03:48.298 12085-12144/com.example.croutinesdemo I/System.out: yup: CoRoutine is running!
2020-09-15 23:03:49.301 12085-12144/com.example.croutinesdemo I/System.out: yup: CoRoutine is running!
2020-09-15 23:03:50.313 12085-12144/com.example.croutinesdemo I/System.out: yup: CoRoutine is running!
2020-09-15 23:03:51.316 12085-12144/com.example.croutinesdemo I/System.out: yup: CoRoutine is running!
2020-09-15 23:03:52.322 12085-12085/com.example.croutinesdemo I/System.out: yup: main thread continuing!
* */
val job = GlobalScope.launch(Dispatchers.Default) {
repeat(5) {
println("yup: CoRoutine is running!")
delay(1000L)
}
}
runBlocking {
job.join()
println("yup: main thread continuing!")
}
}
private fun runBlockingDemo4() {
// 1
// create coroutine in main thread or whatever thread, and blocks the respective thread
// println("yup before run blocking")
// runBlocking {
// println("yup inside run blocking, before delay")
// delay(5000L)
// println("yup inside run blocking, after delay")
// }
// println("yup after run blocking")
// 2
// runBlocking {
// /*
// * o/p
// * 2020-09-15 21:50:06.557 11180-11180/com.example.croutinesdemo I/System.out: yup hello from main
// 2020-09-15 21:50:06.558 11180-11180/com.example.croutinesdemo I/System.out: yup 2nd inside run blocking, before delay
// 2020-09-15 21:50:09.560 11180-11235/com.example.croutinesdemo I/System.out: yup finished IO co routine
// 2020-09-15 21:50:11.561 11180-11180/com.example.croutinesdemo I/System.out: yup 2nd inside run blocking, after delay
// * */
// launch(Dispatchers.IO) {
// delay(3000L)
// println("yup finished IO co routine")
// }
// println("yup 2nd inside run blocking, before delay")
// delay(5000L)
// println("yup 2nd inside run blocking, after delay")
// }
// 3
runBlocking {
/*
* o/p
2020-09-15 21:51:52.771 11319-11319/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-15 21:51:52.909 11319-11319/com.example.croutinesdemo I/System.out: yup 2nd inside run blocking, before delay
2020-09-15 21:51:57.978 11319-11319/com.example.croutinesdemo I/System.out: yup 2nd inside run blocking, after delay
2020-09-15 21:52:00.987 11319-11371/com.example.croutinesdemo I/System.out: yup finished IO co routine
* */
println("yup 2nd inside run blocking, before delay")
delay(5000L)
launch(Dispatchers.IO) {
delay(3000L)
println("yup finished IO co routine")
}
println("yup 2nd inside run blocking, after delay")
}
}
private fun contextSwitching3() {
GlobalScope.launch(Dispatchers.Main) {
// updating UI
println("yup: I'm CoRoutine that runs in Main Thread: ${Thread.currentThread().name}")
}
GlobalScope.launch(Dispatchers.IO) {
// api calls, db operation, file r/w etc
println("yup: I'm CoRoutine that runs in worker Thread: ${Thread.currentThread().name}")
}
GlobalScope.launch(Dispatchers.Default) {
// such as sorting 10,000 items in list
println("yup: I'm CoRoutine that runs in worker Thread, suitable for long running process: ${Thread.currentThread().name}") // let the thread name = `worker2`
GlobalScope.launch(Dispatchers.Unconfined) {
// not confined to any thread, it'll run in the thread where this launch/suspend function is called
println("yup: I'm CoRoutine that runs in UnConfined Thread: ${Thread.currentThread().name}") // this coroutine will run in thread `worker2`
}
}
// GlobalScope.launch(Dispatchers.Unconfined) {
// // not confined to any thread, it'll run in `main` thread
// println("yup: I'm CoRoutine that runs in UnConfined Thread: ${Thread.currentThread().name}")
// }
GlobalScope.launch(newSingleThreadContext("coroutine will work in this thread!")) {
println("yup: I'm CoRoutine that runs in specified Thread: ${Thread.currentThread().name}")
}
// network call simulation
GlobalScope.launch(Dispatchers.IO) {
println("yup: sim n/w call I'm CoRoutine that runs in worker Thread: ${Thread.currentThread().name}")
val resp = doNetworkCall1()
withContext(Dispatchers.Main) {
println("yup: sim n/w call I'm CoRoutine that runs in main Thread: ${Thread.currentThread().name}")
tvDummy.text = resp
}
}
}
private fun suspendFunc2() {
GlobalScope.launch {
val resp1 = doNetworkCall1()
val resp2 = doNetworkCall2()
println("yup res resp1: $resp1")
println("yup res resp2: $resp2")
}
}
private suspend fun doNetworkCall1(): String {
delay(3000L)
return "this is resp1"
}
private suspend fun doNetworkCall2(): String {
delay(3000L)
return "this is resp2"
}
private fun basicCoroutine1() {
/*
* O/P
*
* 2020-09-15 23:00:56.531 11965-11965/com.example.croutinesdemo I/System.out: yup hello from main
2020-09-15 23:00:59.538 11965-12004/com.example.croutinesdemo I/System.out: yup hello from DefaultDispatcher-worker-1
* */
GlobalScope.launch { // launches co routine, that lives for whole application life time
delay(3000L)
println("yup hello from ${Thread.currentThread().name}")
}
}
}
package com.example.croutinesdemo
import androidx.appcompat.app.AppCompatActivity
class SecondActivity : AppCompatActivity()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment