Skip to content

Instantly share code, notes, and snippets.

@Audhil
Last active September 23, 2020 15:14
Show Gist options
  • Save Audhil/761dd6430946b4dba5e77adff48e488d to your computer and use it in GitHub Desktop.
Save Audhil/761dd6430946b4dba5e77adff48e488d to your computer and use it in GitHub Desktop.
Coroutines in NutShell! - (pay attention to file names)
lifecycleScope.launch(Dispatchers.IO) {
val time = measureTimeMillis {
val res1 = async {
println("yup 1st launched: ${Thread.currentThread().name}")
getNetworkResp1()
}.await()
try {
val res2 = async {
println("yup 2nd launched: ${Thread.currentThread().name}")
getNetworkResp2(res1)
}.await()
println("yup: finally: res1: $res1, res2: $res2")
} catch (e: Exception) {
e.printStackTrace()
}
}
println("yup, it took: $time ms!")
}
private suspend fun getNetworkResp2(res: String): String {
delay(1700)
if (res.equals("res 1", true))
return "res 1"
throw CancellationException("WTF!")
}
private suspend fun getNetworkResp1(): String {
delay(1000)
return "res 1"
}
// based on https://www.youtube.com/watch?v=KWocgiYwwmM&list=PLgCYzUzKIBE_PFBRHFB_aL5stMQg3smhL&index=9&ab_channel=CodingWithMitch
// Coroutine Structured Concurrency, Error Handling and Exceptions
class ThirdActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvDummy.text = "ThirdActivity"
// #1 - if there's exception in any of the child job
doTheDemo1()
}
/*
* #0 - expected flow,
*
* 2020-09-23 01:20:34.922 21687-21835/com.example.croutinesdemo I/System.out: yup: res1: 2
2020-09-23 01:20:35.915 21687-21835/com.example.croutinesdemo I/System.out: yup: res2: 4
2020-09-23 01:20:36.921 21687-21835/com.example.croutinesdemo I/System.out: yup: res3: 6
2020-09-23 01:20:36.921 21687-21835/com.example.croutinesdemo I/System.out: yup, all is well!
* */
/*
* #1 - when there's exception any of the child job - it throws exception in all next jobs, and parent job as well
*
* when exception in 2nd child
* 2020-09-23 01:22:16.005 22169-22338/com.example.croutinesdemo I/System.out: yup: res1: 2
2020-09-23 01:22:17.007 22169-22338/com.example.croutinesdemo I/System.out: yup: Error occured in jobB : java.lang.Exception: its bad to be exception!
2020-09-23 01:22:17.008 22169-22331/com.example.croutinesdemo I/System.out: yup: Error occured in jobC : kotlinx.coroutines.JobCancellationException: Parent job is Cancelling; job=StandaloneCoroutine{Cancelling}@73a4bc9
2020-09-23 01:22:17.009 22169-22338/com.example.croutinesdemo I/System.out: yup exception handled in one of the child job: java.lang.Exception: its bad to be exception!
2020-09-23 01:22:17.009 22169-22338/com.example.croutinesdemo I/System.out: yup: Error ooccured in ParJob: java.lang.Exception: its bad to be exception!
*
*
*
* when exception in 3rd child
* 2020-09-23 01:24:26.687 23069-23233/com.example.croutinesdemo I/System.out: yup: res1: 2
2020-09-23 01:24:27.687 23069-23233/com.example.croutinesdemo I/System.out: yup: res2: 4
2020-09-23 01:24:28.688 23069-23233/com.example.croutinesdemo I/System.out: yup: Error occured in jobC : java.lang.Exception: its bad to be exception!
2020-09-23 01:24:28.689 23069-23233/com.example.croutinesdemo I/System.out: yup exception handled in one of the child job: java.lang.Exception: its bad to be exception!
2020-09-23 01:24:28.689 23069-23233/com.example.croutinesdemo I/System.out: yup: Error ooccured in ParJob: java.lang.Exception: its bad to be exception!
*
*
*
*
*
*
# 2A - when cancellationException is thrown
2020-09-23 01:37:01.952 28783-28952/com.example.croutinesdemo I/System.out: yup: res1: 2
2020-09-23 01:37:02.951 28783-28952/com.example.croutinesdemo I/System.out: yup: Error occurred in jobB : java.util.concurrent.CancellationException: I'm Cancellation exception! at num: 2
2020-09-23 01:37:03.951 28783-28952/com.example.croutinesdemo I/System.out: yup: res3: 6
2020-09-23 01:37:03.952 28783-28952/com.example.croutinesdemo I/System.out: yup, all is well!
*
*
* 2020-09-23 01:38:55.260 29199-29363/com.example.croutinesdemo I/System.out: yup: res1: 2
2020-09-23 01:38:56.258 29199-29363/com.example.croutinesdemo I/System.out: yup: res2: 4
2020-09-23 01:38:57.259 29199-29363/com.example.croutinesdemo I/System.out: yup: Error occurred in jobC : java.util.concurrent.CancellationException: I'm Cancellation exception! at num: 3
2020-09-23 01:38:57.260 29199-29363/com.example.croutinesdemo I/System.out: yup, all is well!
*
*
*
*
* #2B - canceling job purposefully
* 2020-09-23 01:42:33.720 30190-30443/com.example.croutinesdemo I/System.out: yup: Error occurred in jobB : java.util.concurrent.CancellationException: I'm Cancellation: jobB
2020-09-23 01:42:34.526 30190-30442/com.example.croutinesdemo I/System.out: yup: res1: 2
2020-09-23 01:42:36.725 30190-30442/com.example.croutinesdemo I/System.out: yup: res3: 6
2020-09-23 01:42:36.726 30190-30442/com.example.croutinesdemo I/System.out: yup, all is well!
* */
private val exceptionHandler = CoroutineExceptionHandler { _, exp ->
println("yup exception handled within parent or one of the child job: $exp")
}
private fun doTheDemo1() {
val parJob = CoroutineScope(Dispatchers.IO).launch(exceptionHandler) {
// JobA
val jobA = launch {
val res1 = doWork(1)
println("yup: res1: $res1")
}
jobA.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobA : $it")
}
}
// JobB
val jobB = launch {
val res2 = doWork(2)
println("yup: res2: $res2")
}
// #2B - canceling job purposefully
// delay(200)
// jobB.cancel("I'm Cancellation: jobB")
jobB.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobB : $it")
}
}
// JobC
val jobC = launch {
val res3 = doWork(3)
println("yup: res3: $res3")
}
jobC.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobC : $it")
}
}
}
parJob.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in ParJob: $it")
} else
println("yup, all is well!")
}
}
private suspend fun doWork(num: Int): Int {
delay(1000L * num)
// # 1 - exception
// if (num == 2)
// throw Exception("its bad to be exception!")
// # 2A - when cancellationException is thrown
// if (num == 3)
// throw CancellationException("I'm Cancellation exception! at num: $num")
return num * 2
}
}
package com.example.croutinesdemo
import android.os.Bundle
import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_second.*
import kotlinx.coroutines.*
// Coroutine jobs - https://youtu.be/UsHTxOILP5g?list=PLgCYzUzKIBE_PFBRHFB_aL5stMQg3smhL
class SecondActivity : AppCompatActivity() {
private lateinit var job: CompletableJob
private val pMax = 100
private val pMin = 0
private val jobTime = 4000
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
job_button.setOnClickListener {
if (!::job.isInitialized)
initJob()
job_progress_bar.startOrCancel(job)
}
}
private fun initJob() {
job = Job()
job.invokeOnCompletion {
it?.message.run {
if (this.isNullOrBlank()) {
showToast("something went wrong!")
return@run
}
showToast(this)
}
}
job_progress_bar.run {
max = pMax
progress = pMin
}
job_button.run {
text = "start job #1"
}
updateCompletedText("")
}
private fun updateCompletedText(text: String) {
lifecycleScope.launch(Dispatchers.Main) {
job_complete_text.text = text
}
}
private fun showToast(msg: String) {
lifecycleScope.launch(Dispatchers.Main) {
Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
}
}
private fun ProgressBar.startOrCancel(job: CompletableJob) {
if (this.progress > 0)
resetJob()
else {
job_button.text = "cancel job #1"
lifecycleScope.launch(Dispatchers.IO + job) {
for (i in pMin..pMax) {
delay((jobTime / pMax).toLong())
[email protected] = i
}
updateCompletedText("Job is complete!")
}
}
}
private fun resetJob() {
if (job.isActive || job.isCompleted)
job.cancel(CancellationException("yup it got cancelled!"))
initJob()
}
}
private var parJob: Job? = null
private fun globalScopeDemo1() {
// this works perfectly
/*
* 2020-09-22 01:39:09.724 13358-13358/com.example.croutinesdemo I/System.out: yup: work : 0, done!, in main
2020-09-22 01:39:09.725 13358-13358/com.example.croutinesdemo I/System.out: yup: work : 1, done!, in main
2020-09-22 01:39:09.726 13358-13358/com.example.croutinesdemo I/System.out: yup: it took: 1 ms!
*
on tapping cancel
* 2020-09-22 01:40:27.717 13358-13358/com.example.croutinesdemo I/System.out: yup job canceled: StandaloneCoroutine was cancelled, it took: 0 ms!
* */
// click to cancel parent job
tvDummy.setOnClickListener {
parJob?.cancel()
}
val time = measureTimeMillis {
parJob = lifecycleScope.launch {
// this coroutines runs in parallel
launch {
work(0)
}
// this too
launch {
work(1)
}
}
}
parJob?.invokeOnCompletion {
if (it != null) {
println("yup job canceled: ${it.message}, it took: $time ms!")
} else {
println("yup: it took: $time ms!")
}
}
}
private fun globalScopeDemo2() {
// parent job gets completed, then child jobs executes
/*
2020-09-22 01:43:01.346 14435-14435/com.example.croutinesdemo I/System.out: yup: it took: 1 ms!
2020-09-22 01:43:04.354 14435-14531/com.example.croutinesdemo I/System.out: yup: work : 0, done!, in DefaultDispatcher-worker-3
2020-09-22 01:43:04.354 14435-14534/com.example.croutinesdemo I/System.out: yup: work : 1, done!, in DefaultDispatcher-worker-5
*
on tapping cancel, the child jobs keeps executing! - don't use GlobalScopes!
2020-09-22 01:43:59.926 14435-14435/com.example.croutinesdemo I/System.out: yup: it took: 1 ms!
2020-09-22 01:44:02.928 14435-14531/com.example.croutinesdemo I/System.out: yup: work : 0, done!, in DefaultDispatcher-worker-3
2020-09-22 01:44:02.928 14435-14534/com.example.croutinesdemo I/System.out: yup: work : 1, done!, in DefaultDispatcher-worker-5
* */
tvDummy.setOnClickListener {
parJob?.cancel()
}
val time = measureTimeMillis {
parJob = lifecycleScope.launch {
// this coroutines runs as individual - doesn't care about parent job
GlobalScope.launch {
work(0)
}
// this too
GlobalScope.launch {
work(1)
}
}
}
parJob?.invokeOnCompletion {
if (it != null) {
println("yup job canceled: ${it.message}, it took: $time ms!")
} else {
println("yup: it took: $time ms!")
}
}
}
private suspend fun work(i: Int) {
delay(3000)
println("yup: work : $i, done!, in ${Thread.currentThread().name}")
}
// 1 million coroutines
val c = AtomicLong()
for (i in 1..1000_000L) {
lifecycleScope.launch {
c.addAndGet(i)
}
}
println("yup: finally: ${c.get()}") // 500000500000 - possible that main thread will complete before any coroutine completes
// 1 million coroutines with async & await
val df = (1..1000_000L).map {
lifecycleScope.async {
it
}
}
val sumIs = runBlocking {
df.map {
it.await()
}.sum()
}
println("yup: it is sum: $sumIs") // 500000500000 - main thread will wait until all the coroutine completes, runBlocking{} is used only to execute await() function in main thread
// 1 million coroutines with some delay - coroutines runs in parallel - it didn't work on my phone
val dff = (1..1000_000L).map {
lifecycleScope.async {
delay(1000)
it
}
}
val sumIss = runBlocking {
dff.map {
it.await()
}.sum()
}
println("yup: it is sumIss: $sumIss") // 500000500000 - main thread will wait until all the coroutine completes, runBlocking{} is used only to execute await() function in main thread
class ThirdActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvDummy.text = "ThirdActivity"
// #1 - if there's exception in any of the child job
// doTheDemo1()
// doTheDemo2()
}
// #B - when exception handler passed to child job
private fun doTheDemo2() {
/*
*
* 2020-09-23 20:39:49.838 17816-17914/com.example.croutinesdemo I/System.out: yup: res1: 2
2020-09-23 20:39:50.839 17816-17914/com.example.croutinesdemo I/System.out: yup exception handled within parent or one of the child job: java.lang.Exception: its bad to be exception!
2020-09-23 20:39:50.839 17816-17914/com.example.croutinesdemo I/System.out: yup: Error occurred in jobB : java.lang.Exception: its bad to be exception!
2020-09-23 20:39:51.838 17816-17914/com.example.croutinesdemo I/System.out: yup: res3: 6
2020-09-23 20:39:51.839 17816-17914/com.example.croutinesdemo I/System.out: yup, all is well!
*/
val parJob = CoroutineScope(Dispatchers.IO).launch {
// supervisor scope with let the child jobs to handle their own exceptions or flow
supervisorScope {
// JobA
val jobA = launch {
val res1 = doWork(1)
println("yup: res1: $res1")
}
jobA.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobA : $it")
}
}
// JobB
val jobB = launch(exceptionHandler) {
val res2 = doWork(2)
println("yup: res2: $res2")
}
jobB.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobB : $it")
}
}
// JobC
val jobC = launch {
val res3 = doWork(3)
println("yup: res3: $res3")
}
jobC.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobC : $it")
}
}
}
}
parJob.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in ParJob: $it")
} else
println("yup, all is well!")
}
}
private val exceptionHandler = CoroutineExceptionHandler { _, exp ->
println("yup exception handled within parent or one of the child job: $exp")
}
// #A - when exception handler passed to parent job
private fun doTheDemo1() {
/*
*
* 2020-09-23 20:35:24.968 15793-16284/com.example.croutinesdemo I/System.out: yup: res1: 2
2020-09-23 20:35:25.969 15793-16284/com.example.croutinesdemo I/System.out: yup exception handled within parent or one of the child job: java.lang.Exception: its bad to be exception!
2020-09-23 20:35:25.970 15793-16284/com.example.croutinesdemo I/System.out: yup: Error occurred in jobB : java.lang.Exception: its bad to be exception!
2020-09-23 20:35:26.967 15793-16284/com.example.croutinesdemo I/System.out: yup: res3: 6
2020-09-23 20:35:26.968 15793-16284/com.example.croutinesdemo I/System.out: yup, all is well!
* */
val parJob = CoroutineScope(Dispatchers.IO).launch(exceptionHandler) {
// supervisor scope with let the child jobs to handle their own exceptions or flow
supervisorScope {
// JobA
val jobA = launch {
val res1 = doWork(1)
println("yup: res1: $res1")
}
jobA.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobA : $it")
}
}
// JobB
val jobB = launch {
val res2 = doWork(2)
println("yup: res2: $res2")
}
// #2B - canceling job purposefully
// delay(200)
// jobB.cancel("I'm Cancellation: jobB")
jobB.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobB : $it")
}
}
// JobC
val jobC = launch {
val res3 = doWork(3)
println("yup: res3: $res3")
}
jobC.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in jobC : $it")
}
}
}
}
parJob.invokeOnCompletion {
if (it != null) {
println("yup: Error occurred in ParJob: $it")
} else
println("yup, all is well!")
}
}
private suspend fun doWork(num: Int): Int {
delay(1000L * num)
if (num == 2)
throw Exception("its bad to be exception!")
return num * 2
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment