Is the Kotlin Structured Concurrency [coroutine] model scoped to UI suitable for DB writes?












2














I'm specifically concerned about inserting user initiated data into the local database.



The following pattern is prevalent in examples (including from official sources, e.g. JetBrains, Google/Android) for using Kotlin coroutines in conjunction with [Android Architecture Components] ViewModels.



class CoroutineScopedViewModel : ViewModel(), CoroutineScope {
private val _job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + _job

override fun onCleared() {
super.onCleared()
_job.cancel()
}

fun thisIsCalledFromTheUI() = launch {
/* do some UI stuff on the main thread */
withContext(Dispatchers.IO) {
try {
/* do some IO, e.g. inserting into DB */
} catch (error: IOException) {
/* do some exception handling */
}
}
}
}


It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.



But, before I go about refactoring my project from the (pre-1.0.0) globally scoped (launch/async) coroutine model, I feel I need to just have some things clarified:



Is my reading of the documentation correct? Or, will destruction of the viewmodel before the withContext(Dispatchers.IO) block runs to completion trigger cancellation of that job too? I.e. can this model be used for inserting data into my DB, or could some strange timing issue arise where the user hits back or otherwise causes the ViewModel owner to close that ends up losing the data?



I don't want to inadvertently introduce a timing bug because I misunderstood something and therefor converted my code to a model similar to the one shown above.



EDIT:



So, I decided to do a little test, and it seems to me that all those examples using this model to write to the database may have a fundamental bug.



Modifying the code to log what happens, as such:



class ChildViewModel : ViewModel(), CoroutineScope {
private val _job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + _job

override fun onCleared() {
super.onCleared()
Log.d("onCleared", "Start")
_job.cancel()
Log.d("onCleared", "End")
}

fun thisIsCalledFromTheUI() = launch {
Log.d("thisIsCalledFromTheUI", "Start")
GlobalScope.launch(Dispatchers.IO) {
Log.d("GlobalScope", "Start")
delay(15000)
Log.d("GlobalScope", "End")
}
withContext(Dispatchers.IO) {
Log.d("withContext", "Start")
delay(10000)
Log.d("withContext", "End")
}
Log.d("thisIsCalledFromTheUI", "End")
}
}


Results in this, if you let it run to completion:



D/thisIsCalledFromTheUI: Start
D/GlobalScope: Start
D/withContext: Start
D/withContext: End
D/thisIsCalledFromTheUI: End
D/GlobalScope: End


But, if you close the Fragment/Activity (not the app) before withContext ends, you get this:



D/thisIsCalledFromTheUI: Start
D/GlobalScope: Start
D/withContext: Start
D/GlobalScope: End


Which indicates, to me at least, that you cannot use this to write non-transient data to the DB.










share|improve this question





























    2














    I'm specifically concerned about inserting user initiated data into the local database.



    The following pattern is prevalent in examples (including from official sources, e.g. JetBrains, Google/Android) for using Kotlin coroutines in conjunction with [Android Architecture Components] ViewModels.



    class CoroutineScopedViewModel : ViewModel(), CoroutineScope {
    private val _job = Job()
    override val coroutineContext: CoroutineContext
    get() = Dispatchers.Main + _job

    override fun onCleared() {
    super.onCleared()
    _job.cancel()
    }

    fun thisIsCalledFromTheUI() = launch {
    /* do some UI stuff on the main thread */
    withContext(Dispatchers.IO) {
    try {
    /* do some IO, e.g. inserting into DB */
    } catch (error: IOException) {
    /* do some exception handling */
    }
    }
    }
    }


    It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.



    But, before I go about refactoring my project from the (pre-1.0.0) globally scoped (launch/async) coroutine model, I feel I need to just have some things clarified:



    Is my reading of the documentation correct? Or, will destruction of the viewmodel before the withContext(Dispatchers.IO) block runs to completion trigger cancellation of that job too? I.e. can this model be used for inserting data into my DB, or could some strange timing issue arise where the user hits back or otherwise causes the ViewModel owner to close that ends up losing the data?



    I don't want to inadvertently introduce a timing bug because I misunderstood something and therefor converted my code to a model similar to the one shown above.



    EDIT:



    So, I decided to do a little test, and it seems to me that all those examples using this model to write to the database may have a fundamental bug.



    Modifying the code to log what happens, as such:



    class ChildViewModel : ViewModel(), CoroutineScope {
    private val _job = Job()
    override val coroutineContext: CoroutineContext
    get() = Dispatchers.Main + _job

    override fun onCleared() {
    super.onCleared()
    Log.d("onCleared", "Start")
    _job.cancel()
    Log.d("onCleared", "End")
    }

    fun thisIsCalledFromTheUI() = launch {
    Log.d("thisIsCalledFromTheUI", "Start")
    GlobalScope.launch(Dispatchers.IO) {
    Log.d("GlobalScope", "Start")
    delay(15000)
    Log.d("GlobalScope", "End")
    }
    withContext(Dispatchers.IO) {
    Log.d("withContext", "Start")
    delay(10000)
    Log.d("withContext", "End")
    }
    Log.d("thisIsCalledFromTheUI", "End")
    }
    }


    Results in this, if you let it run to completion:



    D/thisIsCalledFromTheUI: Start
    D/GlobalScope: Start
    D/withContext: Start
    D/withContext: End
    D/thisIsCalledFromTheUI: End
    D/GlobalScope: End


    But, if you close the Fragment/Activity (not the app) before withContext ends, you get this:



    D/thisIsCalledFromTheUI: Start
    D/GlobalScope: Start
    D/withContext: Start
    D/GlobalScope: End


    Which indicates, to me at least, that you cannot use this to write non-transient data to the DB.










    share|improve this question



























      2












      2








      2







      I'm specifically concerned about inserting user initiated data into the local database.



      The following pattern is prevalent in examples (including from official sources, e.g. JetBrains, Google/Android) for using Kotlin coroutines in conjunction with [Android Architecture Components] ViewModels.



      class CoroutineScopedViewModel : ViewModel(), CoroutineScope {
      private val _job = Job()
      override val coroutineContext: CoroutineContext
      get() = Dispatchers.Main + _job

      override fun onCleared() {
      super.onCleared()
      _job.cancel()
      }

      fun thisIsCalledFromTheUI() = launch {
      /* do some UI stuff on the main thread */
      withContext(Dispatchers.IO) {
      try {
      /* do some IO, e.g. inserting into DB */
      } catch (error: IOException) {
      /* do some exception handling */
      }
      }
      }
      }


      It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.



      But, before I go about refactoring my project from the (pre-1.0.0) globally scoped (launch/async) coroutine model, I feel I need to just have some things clarified:



      Is my reading of the documentation correct? Or, will destruction of the viewmodel before the withContext(Dispatchers.IO) block runs to completion trigger cancellation of that job too? I.e. can this model be used for inserting data into my DB, or could some strange timing issue arise where the user hits back or otherwise causes the ViewModel owner to close that ends up losing the data?



      I don't want to inadvertently introduce a timing bug because I misunderstood something and therefor converted my code to a model similar to the one shown above.



      EDIT:



      So, I decided to do a little test, and it seems to me that all those examples using this model to write to the database may have a fundamental bug.



      Modifying the code to log what happens, as such:



      class ChildViewModel : ViewModel(), CoroutineScope {
      private val _job = Job()
      override val coroutineContext: CoroutineContext
      get() = Dispatchers.Main + _job

      override fun onCleared() {
      super.onCleared()
      Log.d("onCleared", "Start")
      _job.cancel()
      Log.d("onCleared", "End")
      }

      fun thisIsCalledFromTheUI() = launch {
      Log.d("thisIsCalledFromTheUI", "Start")
      GlobalScope.launch(Dispatchers.IO) {
      Log.d("GlobalScope", "Start")
      delay(15000)
      Log.d("GlobalScope", "End")
      }
      withContext(Dispatchers.IO) {
      Log.d("withContext", "Start")
      delay(10000)
      Log.d("withContext", "End")
      }
      Log.d("thisIsCalledFromTheUI", "End")
      }
      }


      Results in this, if you let it run to completion:



      D/thisIsCalledFromTheUI: Start
      D/GlobalScope: Start
      D/withContext: Start
      D/withContext: End
      D/thisIsCalledFromTheUI: End
      D/GlobalScope: End


      But, if you close the Fragment/Activity (not the app) before withContext ends, you get this:



      D/thisIsCalledFromTheUI: Start
      D/GlobalScope: Start
      D/withContext: Start
      D/GlobalScope: End


      Which indicates, to me at least, that you cannot use this to write non-transient data to the DB.










      share|improve this question















      I'm specifically concerned about inserting user initiated data into the local database.



      The following pattern is prevalent in examples (including from official sources, e.g. JetBrains, Google/Android) for using Kotlin coroutines in conjunction with [Android Architecture Components] ViewModels.



      class CoroutineScopedViewModel : ViewModel(), CoroutineScope {
      private val _job = Job()
      override val coroutineContext: CoroutineContext
      get() = Dispatchers.Main + _job

      override fun onCleared() {
      super.onCleared()
      _job.cancel()
      }

      fun thisIsCalledFromTheUI() = launch {
      /* do some UI stuff on the main thread */
      withContext(Dispatchers.IO) {
      try {
      /* do some IO, e.g. inserting into DB */
      } catch (error: IOException) {
      /* do some exception handling */
      }
      }
      }
      }


      It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.



      But, before I go about refactoring my project from the (pre-1.0.0) globally scoped (launch/async) coroutine model, I feel I need to just have some things clarified:



      Is my reading of the documentation correct? Or, will destruction of the viewmodel before the withContext(Dispatchers.IO) block runs to completion trigger cancellation of that job too? I.e. can this model be used for inserting data into my DB, or could some strange timing issue arise where the user hits back or otherwise causes the ViewModel owner to close that ends up losing the data?



      I don't want to inadvertently introduce a timing bug because I misunderstood something and therefor converted my code to a model similar to the one shown above.



      EDIT:



      So, I decided to do a little test, and it seems to me that all those examples using this model to write to the database may have a fundamental bug.



      Modifying the code to log what happens, as such:



      class ChildViewModel : ViewModel(), CoroutineScope {
      private val _job = Job()
      override val coroutineContext: CoroutineContext
      get() = Dispatchers.Main + _job

      override fun onCleared() {
      super.onCleared()
      Log.d("onCleared", "Start")
      _job.cancel()
      Log.d("onCleared", "End")
      }

      fun thisIsCalledFromTheUI() = launch {
      Log.d("thisIsCalledFromTheUI", "Start")
      GlobalScope.launch(Dispatchers.IO) {
      Log.d("GlobalScope", "Start")
      delay(15000)
      Log.d("GlobalScope", "End")
      }
      withContext(Dispatchers.IO) {
      Log.d("withContext", "Start")
      delay(10000)
      Log.d("withContext", "End")
      }
      Log.d("thisIsCalledFromTheUI", "End")
      }
      }


      Results in this, if you let it run to completion:



      D/thisIsCalledFromTheUI: Start
      D/GlobalScope: Start
      D/withContext: Start
      D/withContext: End
      D/thisIsCalledFromTheUI: End
      D/GlobalScope: End


      But, if you close the Fragment/Activity (not the app) before withContext ends, you get this:



      D/thisIsCalledFromTheUI: Start
      D/GlobalScope: Start
      D/withContext: Start
      D/GlobalScope: End


      Which indicates, to me at least, that you cannot use this to write non-transient data to the DB.







      android kotlin android-architecture-components kotlinx.coroutines android-viewmodel






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 16 at 4:59

























      asked Nov 16 at 3:02









      Andre Artus

      1,4891118




      1,4891118
























          1 Answer
          1






          active

          oldest

          votes


















          1















          It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.




          This isn't a correct reading of the documentation. withContext doesn't start another coroutine, it just changes the current coroutine's context for the duration of its block. Therefore this coroutine will get cancelled, as well as all other coroutines you start without providing a new parent context that has a different job associated with it (or no job at all, like the GlobalScope).



          However, your proposed idea to use the GlobalScope for persistent operations is just a local patch for the scenario you're testing, you're still not getting a guarantee it will run to completion. The user can exit the application completely and Android can kill the process.



          Therefore, if your goal is building a truly robust application, you must accommodate the fact that, until the coroutine completes, no information was written to the DB. Hopefully you run the operation within a DB transaction that will automatically roll back if your program gets killed, otherwise it will be impossible to prevent inconsistencies.






          share|improve this answer





















          • Thanks, that makes sense but why are so many examples using it?. In practice I'm keeping the IO short, writing to the DB and sending it to the back-end (the truly time-consuming operation) using the AAC WorkManager framework. Yes, every relevant DB operation (insert, update) is done in a transaction. An application exit is less concerning to me, as that is something the user is likely to notice, and deal with. The user may have to travel a day to acquire the single datum, don't want a too quick back press or finish to necessitate a redo (if it goes unnoticed until they get back).
            – Andre Artus
            Nov 16 at 15:00












          • This is the sentence from the docs that confuses me: "A long-running coroutine, performing some IO or a background computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore" It leaves the impression that one only wants to get rid of the unnecessary UI elements, not get rid of the IO operation.
            – Andre Artus
            Nov 16 at 15:06










          • Examples use GlobalScope because that's what it's for---self-contained examples that you can run standalone. I made it a point to never answer a question here using GlobalScope, no matter how simple the code is that I want to show.
            – Marko Topolnik
            Nov 16 at 15:08






          • 1




            If your DB operation doesn't eventually interact with the GUI in the initiating activity, you don't have to bind it to its lifecycle. For that case you can introduce an application-level scope, bound to the lifecycle of YouApplication. This is still better than GlobalScope. It allows you to specify Dispatchers.Main as the default dispatcher.
            – Marko Topolnik
            Nov 16 at 15:10






          • 1




            Dispatchers.IO is strictly for making blocking API calls. It makes sense when updating the ViewModel from the results of such a call, but otherwise it's pointless and wrong.
            – Marko Topolnik
            Nov 16 at 18:03











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53330816%2fis-the-kotlin-structured-concurrency-coroutine-model-scoped-to-ui-suitable-for%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1















          It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.




          This isn't a correct reading of the documentation. withContext doesn't start another coroutine, it just changes the current coroutine's context for the duration of its block. Therefore this coroutine will get cancelled, as well as all other coroutines you start without providing a new parent context that has a different job associated with it (or no job at all, like the GlobalScope).



          However, your proposed idea to use the GlobalScope for persistent operations is just a local patch for the scenario you're testing, you're still not getting a guarantee it will run to completion. The user can exit the application completely and Android can kill the process.



          Therefore, if your goal is building a truly robust application, you must accommodate the fact that, until the coroutine completes, no information was written to the DB. Hopefully you run the operation within a DB transaction that will automatically roll back if your program gets killed, otherwise it will be impossible to prevent inconsistencies.






          share|improve this answer





















          • Thanks, that makes sense but why are so many examples using it?. In practice I'm keeping the IO short, writing to the DB and sending it to the back-end (the truly time-consuming operation) using the AAC WorkManager framework. Yes, every relevant DB operation (insert, update) is done in a transaction. An application exit is less concerning to me, as that is something the user is likely to notice, and deal with. The user may have to travel a day to acquire the single datum, don't want a too quick back press or finish to necessitate a redo (if it goes unnoticed until they get back).
            – Andre Artus
            Nov 16 at 15:00












          • This is the sentence from the docs that confuses me: "A long-running coroutine, performing some IO or a background computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore" It leaves the impression that one only wants to get rid of the unnecessary UI elements, not get rid of the IO operation.
            – Andre Artus
            Nov 16 at 15:06










          • Examples use GlobalScope because that's what it's for---self-contained examples that you can run standalone. I made it a point to never answer a question here using GlobalScope, no matter how simple the code is that I want to show.
            – Marko Topolnik
            Nov 16 at 15:08






          • 1




            If your DB operation doesn't eventually interact with the GUI in the initiating activity, you don't have to bind it to its lifecycle. For that case you can introduce an application-level scope, bound to the lifecycle of YouApplication. This is still better than GlobalScope. It allows you to specify Dispatchers.Main as the default dispatcher.
            – Marko Topolnik
            Nov 16 at 15:10






          • 1




            Dispatchers.IO is strictly for making blocking API calls. It makes sense when updating the ViewModel from the results of such a call, but otherwise it's pointless and wrong.
            – Marko Topolnik
            Nov 16 at 18:03
















          1















          It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.




          This isn't a correct reading of the documentation. withContext doesn't start another coroutine, it just changes the current coroutine's context for the duration of its block. Therefore this coroutine will get cancelled, as well as all other coroutines you start without providing a new parent context that has a different job associated with it (or no job at all, like the GlobalScope).



          However, your proposed idea to use the GlobalScope for persistent operations is just a local patch for the scenario you're testing, you're still not getting a guarantee it will run to completion. The user can exit the application completely and Android can kill the process.



          Therefore, if your goal is building a truly robust application, you must accommodate the fact that, until the coroutine completes, no information was written to the DB. Hopefully you run the operation within a DB transaction that will automatically roll back if your program gets killed, otherwise it will be impossible to prevent inconsistencies.






          share|improve this answer





















          • Thanks, that makes sense but why are so many examples using it?. In practice I'm keeping the IO short, writing to the DB and sending it to the back-end (the truly time-consuming operation) using the AAC WorkManager framework. Yes, every relevant DB operation (insert, update) is done in a transaction. An application exit is less concerning to me, as that is something the user is likely to notice, and deal with. The user may have to travel a day to acquire the single datum, don't want a too quick back press or finish to necessitate a redo (if it goes unnoticed until they get back).
            – Andre Artus
            Nov 16 at 15:00












          • This is the sentence from the docs that confuses me: "A long-running coroutine, performing some IO or a background computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore" It leaves the impression that one only wants to get rid of the unnecessary UI elements, not get rid of the IO operation.
            – Andre Artus
            Nov 16 at 15:06










          • Examples use GlobalScope because that's what it's for---self-contained examples that you can run standalone. I made it a point to never answer a question here using GlobalScope, no matter how simple the code is that I want to show.
            – Marko Topolnik
            Nov 16 at 15:08






          • 1




            If your DB operation doesn't eventually interact with the GUI in the initiating activity, you don't have to bind it to its lifecycle. For that case you can introduce an application-level scope, bound to the lifecycle of YouApplication. This is still better than GlobalScope. It allows you to specify Dispatchers.Main as the default dispatcher.
            – Marko Topolnik
            Nov 16 at 15:10






          • 1




            Dispatchers.IO is strictly for making blocking API calls. It makes sense when updating the ViewModel from the results of such a call, but otherwise it's pointless and wrong.
            – Marko Topolnik
            Nov 16 at 18:03














          1












          1








          1







          It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.




          This isn't a correct reading of the documentation. withContext doesn't start another coroutine, it just changes the current coroutine's context for the duration of its block. Therefore this coroutine will get cancelled, as well as all other coroutines you start without providing a new parent context that has a different job associated with it (or no job at all, like the GlobalScope).



          However, your proposed idea to use the GlobalScope for persistent operations is just a local patch for the scenario you're testing, you're still not getting a guarantee it will run to completion. The user can exit the application completely and Android can kill the process.



          Therefore, if your goal is building a truly robust application, you must accommodate the fact that, until the coroutine completes, no information was written to the DB. Hopefully you run the operation within a DB transaction that will automatically roll back if your program gets killed, otherwise it will be impossible to prevent inconsistencies.






          share|improve this answer













          It's my understanding of the documentation that in the above example the coroutines started in the UI context (defined through coroutineContext) will be cancelled when the ViewModel is destroyed, but that the code in the withContext(Dispatchers.IO) block will get to run to completion.




          This isn't a correct reading of the documentation. withContext doesn't start another coroutine, it just changes the current coroutine's context for the duration of its block. Therefore this coroutine will get cancelled, as well as all other coroutines you start without providing a new parent context that has a different job associated with it (or no job at all, like the GlobalScope).



          However, your proposed idea to use the GlobalScope for persistent operations is just a local patch for the scenario you're testing, you're still not getting a guarantee it will run to completion. The user can exit the application completely and Android can kill the process.



          Therefore, if your goal is building a truly robust application, you must accommodate the fact that, until the coroutine completes, no information was written to the DB. Hopefully you run the operation within a DB transaction that will automatically roll back if your program gets killed, otherwise it will be impossible to prevent inconsistencies.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 16 at 9:39









          Marko Topolnik

          145k19194321




          145k19194321












          • Thanks, that makes sense but why are so many examples using it?. In practice I'm keeping the IO short, writing to the DB and sending it to the back-end (the truly time-consuming operation) using the AAC WorkManager framework. Yes, every relevant DB operation (insert, update) is done in a transaction. An application exit is less concerning to me, as that is something the user is likely to notice, and deal with. The user may have to travel a day to acquire the single datum, don't want a too quick back press or finish to necessitate a redo (if it goes unnoticed until they get back).
            – Andre Artus
            Nov 16 at 15:00












          • This is the sentence from the docs that confuses me: "A long-running coroutine, performing some IO or a background computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore" It leaves the impression that one only wants to get rid of the unnecessary UI elements, not get rid of the IO operation.
            – Andre Artus
            Nov 16 at 15:06










          • Examples use GlobalScope because that's what it's for---self-contained examples that you can run standalone. I made it a point to never answer a question here using GlobalScope, no matter how simple the code is that I want to show.
            – Marko Topolnik
            Nov 16 at 15:08






          • 1




            If your DB operation doesn't eventually interact with the GUI in the initiating activity, you don't have to bind it to its lifecycle. For that case you can introduce an application-level scope, bound to the lifecycle of YouApplication. This is still better than GlobalScope. It allows you to specify Dispatchers.Main as the default dispatcher.
            – Marko Topolnik
            Nov 16 at 15:10






          • 1




            Dispatchers.IO is strictly for making blocking API calls. It makes sense when updating the ViewModel from the results of such a call, but otherwise it's pointless and wrong.
            – Marko Topolnik
            Nov 16 at 18:03


















          • Thanks, that makes sense but why are so many examples using it?. In practice I'm keeping the IO short, writing to the DB and sending it to the back-end (the truly time-consuming operation) using the AAC WorkManager framework. Yes, every relevant DB operation (insert, update) is done in a transaction. An application exit is less concerning to me, as that is something the user is likely to notice, and deal with. The user may have to travel a day to acquire the single datum, don't want a too quick back press or finish to necessitate a redo (if it goes unnoticed until they get back).
            – Andre Artus
            Nov 16 at 15:00












          • This is the sentence from the docs that confuses me: "A long-running coroutine, performing some IO or a background computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore" It leaves the impression that one only wants to get rid of the unnecessary UI elements, not get rid of the IO operation.
            – Andre Artus
            Nov 16 at 15:06










          • Examples use GlobalScope because that's what it's for---self-contained examples that you can run standalone. I made it a point to never answer a question here using GlobalScope, no matter how simple the code is that I want to show.
            – Marko Topolnik
            Nov 16 at 15:08






          • 1




            If your DB operation doesn't eventually interact with the GUI in the initiating activity, you don't have to bind it to its lifecycle. For that case you can introduce an application-level scope, bound to the lifecycle of YouApplication. This is still better than GlobalScope. It allows you to specify Dispatchers.Main as the default dispatcher.
            – Marko Topolnik
            Nov 16 at 15:10






          • 1




            Dispatchers.IO is strictly for making blocking API calls. It makes sense when updating the ViewModel from the results of such a call, but otherwise it's pointless and wrong.
            – Marko Topolnik
            Nov 16 at 18:03
















          Thanks, that makes sense but why are so many examples using it?. In practice I'm keeping the IO short, writing to the DB and sending it to the back-end (the truly time-consuming operation) using the AAC WorkManager framework. Yes, every relevant DB operation (insert, update) is done in a transaction. An application exit is less concerning to me, as that is something the user is likely to notice, and deal with. The user may have to travel a day to acquire the single datum, don't want a too quick back press or finish to necessitate a redo (if it goes unnoticed until they get back).
          – Andre Artus
          Nov 16 at 15:00






          Thanks, that makes sense but why are so many examples using it?. In practice I'm keeping the IO short, writing to the DB and sending it to the back-end (the truly time-consuming operation) using the AAC WorkManager framework. Yes, every relevant DB operation (insert, update) is done in a transaction. An application exit is less concerning to me, as that is something the user is likely to notice, and deal with. The user may have to travel a day to acquire the single datum, don't want a too quick back press or finish to necessitate a redo (if it goes unnoticed until they get back).
          – Andre Artus
          Nov 16 at 15:00














          This is the sentence from the docs that confuses me: "A long-running coroutine, performing some IO or a background computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore" It leaves the impression that one only wants to get rid of the unnecessary UI elements, not get rid of the IO operation.
          – Andre Artus
          Nov 16 at 15:06




          This is the sentence from the docs that confuses me: "A long-running coroutine, performing some IO or a background computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore" It leaves the impression that one only wants to get rid of the unnecessary UI elements, not get rid of the IO operation.
          – Andre Artus
          Nov 16 at 15:06












          Examples use GlobalScope because that's what it's for---self-contained examples that you can run standalone. I made it a point to never answer a question here using GlobalScope, no matter how simple the code is that I want to show.
          – Marko Topolnik
          Nov 16 at 15:08




          Examples use GlobalScope because that's what it's for---self-contained examples that you can run standalone. I made it a point to never answer a question here using GlobalScope, no matter how simple the code is that I want to show.
          – Marko Topolnik
          Nov 16 at 15:08




          1




          1




          If your DB operation doesn't eventually interact with the GUI in the initiating activity, you don't have to bind it to its lifecycle. For that case you can introduce an application-level scope, bound to the lifecycle of YouApplication. This is still better than GlobalScope. It allows you to specify Dispatchers.Main as the default dispatcher.
          – Marko Topolnik
          Nov 16 at 15:10




          If your DB operation doesn't eventually interact with the GUI in the initiating activity, you don't have to bind it to its lifecycle. For that case you can introduce an application-level scope, bound to the lifecycle of YouApplication. This is still better than GlobalScope. It allows you to specify Dispatchers.Main as the default dispatcher.
          – Marko Topolnik
          Nov 16 at 15:10




          1




          1




          Dispatchers.IO is strictly for making blocking API calls. It makes sense when updating the ViewModel from the results of such a call, but otherwise it's pointless and wrong.
          – Marko Topolnik
          Nov 16 at 18:03




          Dispatchers.IO is strictly for making blocking API calls. It makes sense when updating the ViewModel from the results of such a call, but otherwise it's pointless and wrong.
          – Marko Topolnik
          Nov 16 at 18:03


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53330816%2fis-the-kotlin-structured-concurrency-coroutine-model-scoped-to-ui-suitable-for%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

          ComboBox Display Member on multiple fields

          Is it possible to collect Nectar points via Trainline?