My understanding of why a coroutine that calls delay() is cancellable is not because it suspends the coroutine for some minimum required time to allow cancellation but, rather, because all suspend functions of the kotlinx coroutine library check for cancellation in their implementations. In other words, they "cooperate" with cancellation just as you demonstrated with the Fibonacci example.
Thank you for a helpful video. Since you asked for suggestions: At 1:10, you said that the join() function would block the thread. That seems misleading. AFAIU, job.join() itself doesn't block anything; it just suspends the current coroutine until the job is completed. On the other hand, block the current thread, until the coroutine inside it completes (and join() suspends that coroutine, which indirectly happens to keep the thread blocked in this case). You might want to reword this so people don't get confused.
Nice overview! Thanks! Suggestion (since you asked!): I wish you would go into some details on how coroutines are implemented in Kotlin. This will help understand coroutines better since we'll have some idea of what's happening inside the hood.
He did. This is part of a playlist. If you just watch this one without watching the previous videos, then you miss the other details. I would recommend you watch his playlist.
Hi i just wanna say Thank you so much for the videos that you've created for us. Honestly, i bought a course about android and your videos are absolutely better than the course. God bless you
Good Introduction, dude. Instead of GLobalScope, I often create a dedicated coroutine scope and cancel it at onClear method. That can give us greater control on asynchronous jobs and code readability.
Really like way you explain, I have just started kotlin but I'm very confused about how many types of functions kotlin has and ways to execute that functions. I have a knowledge about Java and python but kotlin has many complicated types of functions. Can you make video on functions?
What I understood in 6:55 is that this happens because kotlin compiler seems to set all stack and expects a result from all the stack before stop the job. So doing the if you kind forced the stop before receive the final result of the stack
8:59 the coroutine inside withTimeout is not cancelled even after 3sec. it is just not calling fib() functions because isActive is false. If you want to check just print isActice everytime in loop. you'll see that after 3 sec the corotuine is still executing and false being printed out on LOG. withTimeout is so confusing. i think this coroutine is not cooperative. I dont think adding isActive will make it cooperative
7:10 "because our coroutine does not use a suspend function where it could be paused for some time, there is no pause in which we can tell it..." This sounds like if I would call the fib() code inside a suspend function then it will be cancelled, but if I understood correctly suspend functions are just there to show they can be suspended, I didn't know they are also useful for being able to cancel jobs...
At 9:24, you have cancelled the function with the time out, but does that mean that the globalScope job also gets cancelled? You've cancelled the function inside the job but not the job itself...
From Coroutines documentation: "The scope automatically waits for the completion of all the child coroutines. Therefore, if the scope corresponds to a coroutine, the parent coroutine does not complete until all the coroutines launched in its scope are complete." "When the parent coroutine is cancelled, all its children are recursively cancelled, too. However, this parent-child relation can be explicitly overriden in one of two ways: 1. When a different scope is explicitly specified when launching a coroutine (for example, GlobalScope.launch), then it does not inherit a Job from the parent scope. 2. When a different Job object is passed as the context for the new coroutine (as shown in the example below), then it overrides the Job of the parent scope"
I really like this tutorial. I have one doubt, whether the all coroutine job will cancel if we are canceling this job?. because we created the job instance with GlobalScope right?
Why "Coroutine is running..." is printed 2 times only in the below code: GlobalScope.launch(Dispatchers.Default) { repeat(5) { Log.d(TAG, "Coroutine is running...") } } runBlocking { Log.d(TAG, "Main thread is continuning...") }
Because you don't wait until the coroutine is finished. It will finish the main thread and that also kills all coroutines. Save that job in a variable, call Job.join in runBlocking and it will work
@@PhilippLackner Didn't get what you said. Who is finishing this block of code GlobalScope.launch(Dispatchers.IO) { repeat(5) { Log.d(TAG, "Coroutine is running...") } } And who is finishing the main thread
Ok lol, the reason is that AS summarizes several logs that are the same. If you log "coroutine is running $it" instead, so you print out the current number, it works as expected
@@PhilippLackner Got it.Thanks a lot for your prompt reply . Your tutorial is easy to understand . Please make more tutorial covering latest advanced android topics
Because we don't call any suspend functions inside of the fib function. You only need suspend, if you do stuff in a function that needs to be suspendable (e.g. network calls, db operations, ...)
Hi Philipp, This video 2 years old. even though I just copied your code, it is cancelling perfectly. Could it have been fixed? That would be great if you can answer.
Thanks for video!! I want to append fibonachi output to textView, and lines appended to textView but I didn't see "Ending long running calculation..." on Logcat. Please, explain why "Ending long running calculation..." didn't output on Logcat? Maybe Default thread never gets that line? CODE: val job= GlobalScope.launch(Dispatchers.Default) { Log.d(TAG,"Starting long running calculation...") withTimeout(3000L){ for (i in 30..50){ if(isActive){ val rr=fib(i) Log.d(TAG,"Result for i = $i: $rr") withContext (Dispatchers.Main){ txtView2.append("Result for i = $i: $rr ") } } } } Log.d(TAG,"Ending long running calculation...") } Log.d(TAG,"MAIN thread continue...") } fun fib(n: Int): Long{ return if(n==0) 0 else if(n==1) 1 else fib(n-1) + fib(n-2) } OUTPUT: 2020-12-22 11:46:21.955 30453-30453/eu.tutorials.myapplicationcoroutines2 D/MainActivity: MAIN thread continue... 2020-12-22 11:46:21.956 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Starting long running calculation... 2020-12-22 11:46:21.993 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 30: 832040 2020-12-22 11:46:22.078 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 31: 1346269 2020-12-22 11:46:22.220 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 32: 2178309 2020-12-22 11:46:22.511 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 33: 3524578 2020-12-22 11:46:22.667 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 34: 5702887 2020-12-22 11:46:22.918 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 35: 9227465 2020-12-22 11:46:23.323 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 36: 14930352 2020-12-22 11:46:23.975 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 37: 24157817 2020-12-22 11:46:25.020 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 38: 39088169
Same with me! The reason for this is that the withTimeout-Method throws a TimeoutCancellationException (see comments of the method!). Try this: try { withTimeout(3000) { ... } } catch (e: TimeoutCancellationException) { Log.d(TAG,"TimeoutCancellationException was thrown!") } Log.d(TAG,"Ending long running calculation...")
Hey, Philip. There's something strange happening: The withTimeout function seems to cancel all my coroutine if the code inside its block gets timed out. I'll show you an example: Ex1: GlobalScope.launch(Dispatchers.IO) { Log.i("MainActivity", "Beginning of coroutine") withTimeout(2000L){ delay(2500L) Log.i("MainActivity", "inside timeout block") } Log.i("MainActivity", "out of timeout block") delay(2000L) Log.i("MainActivity", "After delay") } Log result: I/MainActivity: Beginning of coroutine Ex2: GlobalScope.launch(Dispatchers.IO) { Log.i("MainActivity", "Beginning of coroutine") withTimeout(2000L){ delay(1500L) Log.i("MainActivity", "inside timeout block") } Log.i("MainActivity", "out of timeout block") delay(2000L) Log.i("MainActivity", "After delay") } Log result: I/MainActivity: Beginning of coroutine I/MainActivity: inside timeout block I/MainActivity: out of timeout block I/MainActivity: After delay So, as you can see, if the block inside withTimeout gets timed out, the rest of the coroutine gets canceled. It shouldn't work this way, right? Your example at 8:50 seems to work fine. The only difference is you're running your coroutine on Dispatchers.Default. I tried to change the dispatchers too, but the same problem is still happening. Any thoughts on this? withTimeoutOrNull seems to do the job, but I'd like to understand why my code is not working as your example. Btw, thanks for this series! It's very helpful. You're the man!
That's how withTimeout works, it doesn't launch a new coroutine, but instead runs the current coroutine in blocking state. And cancels it if the time exceeds.
@@AbbasAnwerAli Thanks for the reply. But why in Philip's example at 8:50 the Log "Endind long running calculation" is executed? It's inside the same coroutine the block withTimeout is. In my Ex1 the Log.i("MainActivity", "out of timeout block") is not executed.
@@daniboy943 withTimeout launches a new coroutine which u can check by logging out "this". So Abbas' answer isnt correct. And sInce it is launching a new coroutine it should not wait 2000/2500 ms to print the next line after it. According to me after executing withTimeout{ .. } it should launch a new CR and then print "out of timeout block" immediately. Did you find the explanation for this?
i get it now. so what is happening in philipp code is that after 3sec withTimeout tries to cancel the CR but the CR here is not cooperative so it is NOT cancelled (it is still executing). after 3 sec isActive changes from true to false bacuse of which no more fib() calls are made and loop ends. after that it prints the LOG statement. And in your code since you are using delay your CR is cooperative bacuse of which it gets cancelled also. still i dont know why the code after withTimeout is not executing. Maybe its a property of withTimeout. this is what it does, it cancells the CR from which it is called.
Just tried wrapping the withTimeout in try catch and guess what it worked.In your eg 1 it skips the rest of code below it because of TimeoutCancellationException. if you add it in a try catch block you'll see the error message and after that the execution resume from the statement below block of code
Hello Brother, You said in the video that, the launch() function returns a "job". I'm sorry if I sound stupid...but what exactly is a job bro? Thanks in advance :-)
Thanks for the feedback, I actually made a survey on my IG about if I should use light or dark theme and most people wanted me to stick to dark theme, sorry
I would propose this: if job looks like: ``` . . . while(isActive) { // do some work } . . . ``` more or less graceful shutdown: ``` runBlocking { job.cancelAndJoin() delay(2000L) // some time to finish last loop } ```
My understanding of why a coroutine that calls delay() is cancellable is not because it suspends the coroutine for some minimum required time to allow cancellation but, rather, because all suspend functions of the kotlinx coroutine library check for cancellation in their implementations. In other words, they "cooperate" with cancellation just as you demonstrated with the Fibonacci example.
Thank you for a helpful video.
Since you asked for suggestions: At 1:10, you said that the join() function would block the thread. That seems misleading. AFAIU, job.join() itself doesn't block anything; it just suspends the current coroutine until the job is completed. On the other hand, block the current thread, until the coroutine inside it completes (and join() suspends that coroutine, which indirectly happens to keep the thread blocked in this case). You might want to reword this so people don't get confused.
Hands down you have the best coroutine tutorials. Thanks!
Nice overview! Thanks!
Suggestion (since you asked!): I wish you would go into some details on how coroutines are implemented in Kotlin. This will help understand coroutines better since we'll have some idea of what's happening inside the hood.
He did. This is part of a playlist. If you just watch this one without watching the previous videos, then you miss the other details. I would recommend you watch his playlist.
Hi i just wanna say Thank you so much for the videos that you've created for us. Honestly, i bought a course about android and your videos are absolutely better than the course. God bless you
Good Introduction, dude. Instead of GLobalScope, I often create a dedicated coroutine scope and cancel it at onClear method. That can give us greater control on asynchronous jobs and code readability.
God bless you man..!!
Really like way you explain, I have just started kotlin but I'm very confused about how many types of functions kotlin has and ways to execute that functions. I have a knowledge about Java and python but kotlin has many complicated types of functions. Can you make video on functions?
In my Kotlin series I have videos about the most common types of functions
@@PhilippLackner Okk Thanks a lot
I had same issues!
What I understood in 6:55 is that this happens because kotlin compiler seems to set all stack and expects a result from all the stack before stop the job. So doing the if you kind forced the stop before receive the final result of the stack
8:59 the coroutine inside withTimeout is not cancelled even after 3sec. it is just not calling fib() functions because isActive is false. If you want to check just print isActice everytime in loop. you'll see that after 3 sec the corotuine is still executing and false being printed out on LOG. withTimeout is so confusing. i think this coroutine is not cooperative. I dont think adding isActive will make it cooperative
very genuine content
i loved it
Thanks for the tutorial!!!!!!!
7:10 "because our coroutine does not use a suspend function where it could be paused for some time, there is no pause in which we can tell it..."
This sounds like if I would call the fib() code inside a suspend function then it will be cancelled, but if I understood correctly suspend functions are just there to show they can be suspended, I didn't know they are also useful for being able to cancel jobs...
Is it necessary to have isActive inside withTimeout?
U r so awesome man!❤️
It's really very helpful video . Can you please upload the video for coroutines Flow
Just Awesome!!
Thank you so much!
at time 9:07 I think we don't need if (isActive) check.
Thank you Sir 🙌🙌
Philip am thankfull for IOS activiication
At 9:24, you have cancelled the function with the time out, but does that mean that the globalScope job also gets cancelled? You've cancelled the function inside the job but not the job itself...
From Coroutines documentation:
"The scope automatically waits for the completion of all the child coroutines. Therefore, if the scope corresponds to a coroutine, the parent coroutine does not complete until all the coroutines launched in its scope are complete."
"When the parent coroutine is cancelled, all its children are recursively cancelled, too.
However, this parent-child relation can be explicitly overriden in one of two ways:
1. When a different scope is explicitly specified when launching a coroutine (for example, GlobalScope.launch), then it does not inherit a Job from the parent scope.
2. When a different Job object is passed as the context for the new coroutine (as shown in the example below), then it overrides the Job of the parent scope"
Well explained
Thanks!!
Thanks!!!!!!!!
You're welcome!
Please make videos on Apollo Graphql with coroutine mvvm
I really like this tutorial. I have one doubt, whether the all coroutine job will cancel if we are canceling this job?. because we created the job instance with GlobalScope right?
Why "Coroutine is running..." is printed 2 times only in the below code:
GlobalScope.launch(Dispatchers.Default) {
repeat(5) {
Log.d(TAG, "Coroutine is running...")
}
}
runBlocking {
Log.d(TAG, "Main thread is continuning...")
}
Because you don't wait until the coroutine is finished. It will finish the main thread and that also kills all coroutines. Save that job in a variable, call Job.join in runBlocking and it will work
@@PhilippLackner Didn't get what you said. Who is finishing this block of code GlobalScope.launch(Dispatchers.IO) {
repeat(5) {
Log.d(TAG, "Coroutine is running...")
}
}
And who is finishing the main thread
lol nevermind, I thought of another scenario. I just tried it out and it happens for me too, that's very interesting, maybe I can figure out why
Ok lol, the reason is that AS summarizes several logs that are the same. If you log "coroutine is running $it" instead, so you print out the current number, it works as expected
@@PhilippLackner Got it.Thanks a lot for your prompt reply . Your tutorial is easy to understand . Please make more tutorial covering latest advanced android topics
Why the fib is end at nr 37 ?
Is there any better way to cancel the Job instead of calling from runblocking, i mean without blocking mainthread
Is a isActive check still necessary even though we use a coroutine with timeout?
yes
Why did you not use the keyword "suspend" in the fibonacci function? everything else is cristal clear except that, thank you
Because we don't call any suspend functions inside of the fib function. You only need suspend, if you do stuff in a function that needs to be suspendable (e.g. network calls, db operations, ...)
Sir, why you can start a job without use job join in 03:37
Why would you need to join it?
@@PhilippLackner to start it? 😅
@@antoniokomangyudistira4150 I think join function is used to wait for it to complete not for start
Hi Philipp, This video 2 years old. even though I just copied your code, it is cancelling perfectly. Could it have been fixed? That would be great if you can answer.
Nice explanation, next let's do some hacking with coroutine haha
Challenge accepted
Thanks for video!! I want to append fibonachi output to textView, and lines appended to textView but I didn't see "Ending long running calculation..." on Logcat.
Please, explain why "Ending long running calculation..." didn't output on Logcat?
Maybe Default thread never gets that line?
CODE:
val job= GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG,"Starting long running calculation...")
withTimeout(3000L){
for (i in 30..50){
if(isActive){
val rr=fib(i)
Log.d(TAG,"Result for i = $i: $rr")
withContext (Dispatchers.Main){
txtView2.append("Result for i = $i: $rr
")
}
}
}
}
Log.d(TAG,"Ending long running calculation...")
}
Log.d(TAG,"MAIN thread continue...")
}
fun fib(n: Int): Long{
return if(n==0) 0
else if(n==1) 1
else fib(n-1) + fib(n-2)
}
OUTPUT:
2020-12-22 11:46:21.955 30453-30453/eu.tutorials.myapplicationcoroutines2 D/MainActivity: MAIN thread continue...
2020-12-22 11:46:21.956 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Starting long running calculation...
2020-12-22 11:46:21.993 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 30: 832040
2020-12-22 11:46:22.078 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 31: 1346269
2020-12-22 11:46:22.220 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 32: 2178309
2020-12-22 11:46:22.511 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 33: 3524578
2020-12-22 11:46:22.667 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 34: 5702887
2020-12-22 11:46:22.918 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 35: 9227465
2020-12-22 11:46:23.323 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 36: 14930352
2020-12-22 11:46:23.975 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 37: 24157817
2020-12-22 11:46:25.020 30453-30522/eu.tutorials.myapplicationcoroutines2 D/MainActivity: Result for i = 38: 39088169
I have same issue.
Same with me! The reason for this is that the withTimeout-Method throws a TimeoutCancellationException (see comments of the method!).
Try this:
try {
withTimeout(3000) {
...
}
} catch (e: TimeoutCancellationException) {
Log.d(TAG,"TimeoutCancellationException was thrown!")
}
Log.d(TAG,"Ending long running calculation...")
good video
Thanks!
:) Thanks
Do we need still need Alive inside withTimeOut () coz as u said the coroutines execution will stop after 3secs !!
Hey, Philip. There's something strange happening:
The withTimeout function seems to cancel all my coroutine if the code inside its block gets timed out. I'll show you an example:
Ex1:
GlobalScope.launch(Dispatchers.IO) {
Log.i("MainActivity", "Beginning of coroutine")
withTimeout(2000L){
delay(2500L)
Log.i("MainActivity", "inside timeout block")
}
Log.i("MainActivity", "out of timeout block")
delay(2000L)
Log.i("MainActivity", "After delay")
}
Log result:
I/MainActivity: Beginning of coroutine
Ex2:
GlobalScope.launch(Dispatchers.IO) {
Log.i("MainActivity", "Beginning of coroutine")
withTimeout(2000L){
delay(1500L)
Log.i("MainActivity", "inside timeout block")
}
Log.i("MainActivity", "out of timeout block")
delay(2000L)
Log.i("MainActivity", "After delay")
}
Log result:
I/MainActivity: Beginning of coroutine
I/MainActivity: inside timeout block
I/MainActivity: out of timeout block
I/MainActivity: After delay
So, as you can see, if the block inside withTimeout gets timed out, the rest of the coroutine gets canceled. It shouldn't work this way, right? Your example at 8:50 seems to work fine.
The only difference is you're running your coroutine on Dispatchers.Default. I tried to change the dispatchers too, but the same problem is still happening.
Any thoughts on this? withTimeoutOrNull seems to do the job, but I'd like to understand why my code is not working as your example.
Btw, thanks for this series! It's very helpful. You're the man!
That's how withTimeout works, it doesn't launch a new coroutine, but instead runs the current coroutine in blocking state. And cancels it if the time exceeds.
@@AbbasAnwerAli Thanks for the reply.
But why in Philip's example at 8:50 the Log "Endind long running calculation" is executed? It's inside the same coroutine the block withTimeout is.
In my Ex1 the Log.i("MainActivity", "out of timeout block") is not executed.
@@daniboy943 withTimeout launches a new coroutine which u can check by logging out "this". So Abbas' answer isnt correct. And sInce it is launching a new coroutine it should not wait 2000/2500 ms to print the next line after it. According to me after executing withTimeout{ .. } it should launch a new CR and then print "out of timeout block" immediately. Did you find the explanation for this?
i get it now. so what is happening in philipp code is that after 3sec withTimeout tries to cancel the CR but the CR here is not cooperative so it is NOT cancelled (it is still executing). after 3 sec isActive changes from true to false bacuse of which no more fib() calls are made and loop ends. after that it prints the LOG statement. And in your code since you are using delay your CR is cooperative bacuse of which it gets cancelled also. still i dont know why the code after withTimeout is not executing. Maybe its a property of withTimeout. this is what it does, it cancells the CR from which it is called.
Just tried wrapping the withTimeout in try catch and guess what it worked.In your eg 1 it skips the rest of code below it because of TimeoutCancellationException. if you add it in a try catch block you'll see the error message and after that the execution resume from the statement below block of code
You didn't say "I wish you an awesome day" or smt like thaat :'(
damn I fucked up
Hello Brother,
You said in the video that, the launch() function returns a "job".
I'm sorry if I sound stupid...but what exactly is a job bro?
Thanks in advance :-)
A job basically describes a started coroutine. You can use it to cancel the coroutine's work or wait for it to finish for example
@@PhilippLackner and a scope contains multiple jobs right ??
thanks, you make things easier, also if it possible could you please use the android white them, cause it is quite hard to see code in the dark mode
Thanks for the feedback, I actually made a survey on my IG about if I should use light or dark theme and most people wanted me to stick to dark theme, sorry
@@PhilippLackner No problem 😆
+
I would propose this:
if job looks like:
```
. . .
while(isActive) {
// do some work
}
. . .
```
more or less graceful shutdown:
```
runBlocking {
job.cancelAndJoin()
delay(2000L) // some time to finish last loop
}
```