Jobs, Waiting, Cancelation - Kotlin Coroutines

Поделиться
HTML-код
  • Опубликовано: 8 фев 2025

Комментарии • 80

  • @scottloewke5199
    @scottloewke5199 3 года назад +20

    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.

  • @l_a_h797
    @l_a_h797 Год назад +1

    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.

  • @elliot6855
    @elliot6855 4 года назад +12

    Hands down you have the best coroutine tutorials. Thanks!

  • @openenquiry
    @openenquiry 3 года назад +14

    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.

    • @mikiaskebede3849
      @mikiaskebede3849 2 года назад +4

      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.

  • @hafidzshidqi9761
    @hafidzshidqi9761 Год назад

    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

  • @larrystreehouse1124
    @larrystreehouse1124 Год назад +1

    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.

  • @abbasadrali
    @abbasadrali 3 года назад +2

    God bless you man..!!

  • @vinaykumarpatel649
    @vinaykumarpatel649 4 года назад +3

    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?

  • @thepragmaticprogrammer4383
    @thepragmaticprogrammer4383 2 года назад

    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

  • @sameeryadav8857
    @sameeryadav8857 2 года назад +2

    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

  • @ShivamVermaokdone
    @ShivamVermaokdone Год назад

    very genuine content
    i loved it

  • @Kunal-jp8tn
    @Kunal-jp8tn 3 года назад +1

    Thanks for the tutorial!!!!!!!

  • @chagaif
    @chagaif 2 года назад

    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...

  • @Chekist2008
    @Chekist2008 2 месяца назад

    Is it necessary to have isActive inside withTimeout?

  • @aryanchauhan2124
    @aryanchauhan2124 3 года назад

    U r so awesome man!❤️

  • @AbhishekModi08
    @AbhishekModi08 4 года назад

    It's really very helpful video . Can you please upload the video for coroutines Flow

  • @montuedge
    @montuedge 11 месяцев назад

    Just Awesome!!

  • @talhac2796
    @talhac2796 2 года назад

    Thank you so much!

  • @punerealestatebuilder
    @punerealestatebuilder 5 месяцев назад

    at time 9:07 I think we don't need if (isActive) check.

  • @undeadredemption_
    @undeadredemption_ Год назад

    Thank you Sir 🙌🙌

  • @lewekandi784
    @lewekandi784 2 года назад

    Philip am thankfull for IOS activiication

  • @anudeepananth
    @anudeepananth 2 года назад +1

    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...

    • @9iht6ihgt43rzhijj
      @9iht6ihgt43rzhijj 2 года назад +1

      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"

  • @ChrisAthanas
    @ChrisAthanas 4 года назад

    Well explained

  • @abuiman5251
    @abuiman5251 2 года назад

    Thanks!!

  • @maurolanini9891
    @maurolanini9891 2 года назад

    Thanks!!!!!!!!

  • @MrRahulmalik
    @MrRahulmalik 4 года назад

    Please make videos on Apollo Graphql with coroutine mvvm

  • @ashikdev2513
    @ashikdev2513 3 года назад

    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?

  • @rajendranegi5545
    @rajendranegi5545 4 года назад +3

    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...")
    }

    • @PhilippLackner
      @PhilippLackner  4 года назад +3

      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

    • @rajendranegi5545
      @rajendranegi5545 4 года назад

      @@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

    • @PhilippLackner
      @PhilippLackner  4 года назад +2

      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

    • @PhilippLackner
      @PhilippLackner  4 года назад +5

      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

    • @rajendranegi5545
      @rajendranegi5545 4 года назад

      @@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

  • @gigimushi6705
    @gigimushi6705 2 года назад

    Why the fib is end at nr 37 ?

  • @bjugdbjk
    @bjugdbjk 3 года назад

    Is there any better way to cancel the Job instead of calling from runblocking, i mean without blocking mainthread

  • @mehmedmert6679
    @mehmedmert6679 11 месяцев назад

    Is a isActive check still necessary even though we use a coroutine with timeout?

  • @ampo2004
    @ampo2004 4 года назад +1

    Why did you not use the keyword "suspend" in the fibonacci function? everything else is cristal clear except that, thank you

    • @PhilippLackner
      @PhilippLackner  4 года назад +6

      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, ...)

  • @antoniokomangyudistira4150
    @antoniokomangyudistira4150 4 года назад

    Sir, why you can start a job without use job join in 03:37

  • @KotlinBek
    @KotlinBek 2 года назад

    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.

  • @aridwiprayogo
    @aridwiprayogo 4 года назад +3

    Nice explanation, next let's do some hacking with coroutine haha

  • @almazabd8175
    @almazabd8175 4 года назад +1

    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

    • @KoradiyaAbhay
      @KoradiyaAbhay 3 года назад +1

      I have same issue.

    • @martinw.1982
      @martinw.1982 3 года назад

      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...")

  • @jiwomdf8244
    @jiwomdf8244 4 года назад

    good video

  • @ajaygalagali5963
    @ajaygalagali5963 4 года назад

    :) Thanks

  • @GamerSpouse
    @GamerSpouse 4 года назад

    Do we need still need Alive inside withTimeOut () coz as u said the coroutines execution will stop after 3secs !!

  • @daniboy943
    @daniboy943 3 года назад

    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!

    • @AbbasAnwerAli
      @AbbasAnwerAli 2 года назад

      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.

    • @daniboy943
      @daniboy943 2 года назад

      @@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.

    • @sameeryadav8857
      @sameeryadav8857 2 года назад

      @@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?

    • @sameeryadav8857
      @sameeryadav8857 2 года назад

      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.

    • @sameeryadav8857
      @sameeryadav8857 2 года назад

      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

  • @hripsimeghazaryan393
    @hripsimeghazaryan393 4 года назад +4

    You didn't say "I wish you an awesome day" or smt like thaat :'(

  • @brucewayne7909
    @brucewayne7909 4 года назад

    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 :-)

    • @PhilippLackner
      @PhilippLackner  4 года назад +5

      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

    • @syednoorullahshah9194
      @syednoorullahshah9194 4 года назад

      @@PhilippLackner and a scope contains multiple jobs right ??

  • @mohameddarwish1347
    @mohameddarwish1347 4 года назад +1

    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

    • @PhilippLackner
      @PhilippLackner  4 года назад +4

      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

    • @mohameddarwish1347
      @mohameddarwish1347 4 года назад

      @@PhilippLackner No problem 😆

  • @Sk-gb2hx
    @Sk-gb2hx 3 года назад

    +

  • @AlexanderParphenuyk
    @AlexanderParphenuyk 2 года назад

    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
    }
    ```