Is the Kotlin Structured Concurrency [coroutine] model scoped to UI suitable for DB writes?
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
add a comment |
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
add a comment |
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
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
android kotlin android-architecture-components kotlinx.coroutines android-viewmodel
edited Nov 16 at 4:59
asked Nov 16 at 3:02
Andre Artus
1,4891118
1,4891118
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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 theViewModel
is destroyed, but that the code in thewithContext(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.
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 orfinish
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 useGlobalScope
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 usingGlobalScope
, 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 ofYouApplication
. This is still better thanGlobalScope
. It allows you to specifyDispatchers.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 theViewModel
from the results of such a call, but otherwise it's pointless and wrong.
– Marko Topolnik
Nov 16 at 18:03
|
show 3 more comments
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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 theViewModel
is destroyed, but that the code in thewithContext(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.
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 orfinish
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 useGlobalScope
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 usingGlobalScope
, 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 ofYouApplication
. This is still better thanGlobalScope
. It allows you to specifyDispatchers.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 theViewModel
from the results of such a call, but otherwise it's pointless and wrong.
– Marko Topolnik
Nov 16 at 18:03
|
show 3 more comments
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 theViewModel
is destroyed, but that the code in thewithContext(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.
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 orfinish
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 useGlobalScope
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 usingGlobalScope
, 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 ofYouApplication
. This is still better thanGlobalScope
. It allows you to specifyDispatchers.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 theViewModel
from the results of such a call, but otherwise it's pointless and wrong.
– Marko Topolnik
Nov 16 at 18:03
|
show 3 more comments
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 theViewModel
is destroyed, but that the code in thewithContext(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.
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 theViewModel
is destroyed, but that the code in thewithContext(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.
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 orfinish
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 useGlobalScope
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 usingGlobalScope
, 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 ofYouApplication
. This is still better thanGlobalScope
. It allows you to specifyDispatchers.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 theViewModel
from the results of such a call, but otherwise it's pointless and wrong.
– Marko Topolnik
Nov 16 at 18:03
|
show 3 more comments
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 orfinish
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 useGlobalScope
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 usingGlobalScope
, 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 ofYouApplication
. This is still better thanGlobalScope
. It allows you to specifyDispatchers.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 theViewModel
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
|
show 3 more comments
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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