Network call when exiting the screen - Android coroutines - android

I am trying to implement a UI in a fragment, where user can make all sorts of updates and I need send it over to backend when user EXITS the screen. (Batch update)
I am using MVVM pattern, where network calls are performed from viewmodel . Now, viewModelScope.launch won't work here, since as soon as user exits, the coroutine is canceled by onCleared().
For now, I added GlobalScope and it works but I have also come across this and this question
Are there any other alternatives to accomplish this with Coroutines?

Coroutines are mostly recommended for work that should start immediately and is scoped to the lifecycle of a Fragment, Activity, ViewModel or any other object with a lifecycle. Since the rest of the coroutine builders are tied to scopes, they wont accomplish what you are trying to do, since the user might leave your app at any given time.
A better approach would be using WorkManager with CoroutineWorker, which isn't tied to your UIs or App lifespan and still takes the advantages of working with Coroutines. With WorkManager, your work could be enqueued when the user leaves your designated screen and will be guaranteed to run once the constraints you specify are fulfilled (for example having internet connection). I recommend you to check Android's Guide to background processing if you are still making up your mind on which solution to use.

Related

Difference between launchWhenStarted and repeatOnLifecycle(STARTED) in collecting flows

As launchWhenStarted and repeatOnLifecycle(STARTED) provide completely different functionality (launchWhenStarted suspends the execution of the coroutine, and repeatOnLifecycle cancels and restarts a new coroutine), if the names of the new APIs were similar (for example, using launchWhenever for the restarting APIs), developers could’ve got confused and even use them interchangeably without noticing.
source
What is a simpler explanation for when to use which?
launchWhenStarted is just a one-time delay.
repeatOnLifecycle creates a suspending point that acts as a handler that runs provided block every time the lifecycle enters provided state and cancels it whenever it falls below it (so for STARTED it happens when it gets stopped).
Update:
For more information: https://medium.com/androiddevelopers/repeatonlifecycle-api-design-story-8670d1a7d333
repeatOnLifecycle restarts its coroutine from scratch on each repeat, and cancels it each time lifecycle falls below the specified state. It’s a natural fit for collecting most flows, because it fully cancels the flow when it’s not needed, which saves resources related to the flow continuing to emit values.
launchWhenX doesn’t cancel the coroutine and restart it. It just postpones when it starts, and pauses execution while below the specified state. They plan to deprecate these functions but I suspect there will need to be some replacement if they do, for the case where you are calling some time consuming suspend function and then want to do something when it’s done, like starting a fragment transaction. Using repeatOnLifecycle for this would result in redoing the time consuming action.
A cold flow backed by a channel or using operators with buffers such as buffer, conflate, flowOn, or shareIn is not safe to collect with some of the existing APIs such as CoroutineScope.launch, Flow<T>.launchIn, or LifecycleCoroutineScope.launchWhenX, unless you manually cancel the Job that started the coroutine when the activity goes to the background. These APIs will keep the underlying flow producer active while emitting items into the buffer in the background, and thus wasting resources.
To solve this issue with these APIs, you’d need to manually cancel collection when the view goes to the background. but it sounds to be a boilerplate code.
That's why Google recommended repeatOnLifecycle(Lifecycle.State.XXX) to make it simple and safe.
repeatOnLifecycle is a suspend function that takes a Lifecycle.State as a parameter which is used to automatically create and launch a new coroutine with the block passed to it when the lifecycle reaches that state, and cancel the ongoing coroutine that’s executing the block when the lifecycle falls below the state.
Learn more from here

Prevent kotlin coroutine from being interrupted even if user navigates away

In my code, a method performs three API POST and it takes a while for each of those to complete. I'm using kotlin coroutine launch on that method. But I also want the user to be able to navigate away from the page while the task is running. I don't want the task of performing three API calls to be interrupted when navigating away. What is the best way to do that? Is WorkManager the way to go?
Somehow my question is similar to this but I need to do it in Kotlin. Is the answers in that post still relevant?
You need a Service. Please read this:
https://developer.android.com/guide/background

Concurrent Android network requests

I am building a fragment in Android which displays some data kept on a server. As long as the user is on this fragment, I would like to poll the server every x seconds. Additionally, I would like to stop this procedure once the user navigates away from this fragment. Is the optimum solution to this problem to use coroutines or a thread? Thank you for any assistance!
Polling is never a good option as you will keep on wasting resources while the server might have no new data to supply. However, if you are still keen on doing that, you can use any of the choices, i.e. coroutines and thread. In thread, you'd have to manage its lifecycle yourself whereas in coroutine you don't have to worry about anything as CoroutineScope takes care of all that for you. One incentive of going with Coroutines would be the supply of operators you can use on your flows, map and switchMap for instance.
A better solution would be to make use of SNS or firebaseRemoteMessagingService that can notify your application client, upon which you can request the server.

What is the correct way to create a reactive chain for a process that includes UI modal dialogs and long-running tasks?

Reactive programming with RxJava helps create nice and concise code for business processes that include UI dialogs and long-running async tasks. However, Android UI has its peculiarities that can break the process. A simplified example:
public void onProcessWasInitiatedByUser() {
someCompletableSource.startAndWaitForSomeLongProcess()
.flatMap(showModalDialogAndWaitForAnswer())
.flatMap(startAndWaitForSomeOtherLongProcess())
.flatMap(showAnotherModalDialogAndWaitForAnswer())
.subscribe(() -> {
// finalize the process after the user has entered and confirmed all the required data
});
}
Note: subscribeOn() and observeOn() not included in the example for brevity. I know that mixed UI + background processes will require thread switching to avoid UI freezes and direct UI calls from background threads.
The questions:
Considering that startAndWaitForSomeProcess might last long enough for the device to go to sleep, there is a very high chance to get a java.lang.IllegalStateException if attempting to show a modal dialog. How to ensure that modal dialog is displayed when the user unlocks the device, but still to keep the modal dialog as part of this Rx flow?
In theory, I've heard it is recommended to use viewmodels to solve UI lifecycle issues but I'm not sure how it would help in this situation. Ok, I can store the data inside a viewmodel and later show the dialog when the device wakes up... but then how do I continue with the same Rx flow that was started in onProcessWasInitiatedByUser? I would prefer not to break the flow into separate pieces and scatter them around some other event handlers. What should I do inside showModalDialogAndWaitForAnswer function to make them safe for locked screen cases and still wait for the answer from the user?
Less important - I'm not sure if my example is the nicest way to organize this. Is there any rule of thumb, when things should go inside flatMap and when should be left for the final subscribe, or when subscribe should be left entirely empty; or when it's best to use some different RxJava function? How do experienced RxJava developers organize it to avoid ambiguities and possible caveats?
Great questions. Here my observations:
startAndWaitForSomeProcess is an operation that should be executed in the background and a separate context that, once finished, notify the client (View). In this case, what you need is probably a background task or a Service.
Rx helps you to orchestrate both synchronous and asynchronous processes in the same or different Scheduler contexts. I do not recommend using it for this requirement since it would imply an extended UI blocking time, and the operations life cycle would be unmanageable.
It is recommended that conversations with UI be made through the subscribed Observer, not through the operator's chain, as side effects may induce bugs. I've written a useful guide called "The Clean Way to Use Rx," and in Item 33: Negotiating with UI, it talks about it.
I hope that I have helped you.

Android Jetpack: lifecycle-aware recurrent periodic task execution with LiveData and ViewModels

My app already uses some recent Android patterns for network calls:
LiveData class
MVVM architecture with ViewModel class
Kotlin coroutines for Repository classes
Retrofit interface etc.
Now I want to implement common feature which is automatic fetching current data from API every few minutes.
I read about WorkManager and give it a shot - I implemented it but I then saw that WorkManager (JobScheduler) keeps running after closing app which is not what I want. I also felt like WorkManager API is too much overkill for that simple task.
Then I read a guide on Codepath that suggests Handler class for main thread repetitive jobs and ScheduledThreadPoolExecutor for background repetitive tasks. I know that they will probably work fine but I'm not sure if they are best solution.
My question is: what's currently the best way for recurrent API calls that:
works with LiveData and ViewModel classes (observable result like normal API call)
is Kotlin-friendly (any way to make coroutine recurrent?)
is "lifecycle-aware", meaning that it will stop when app goes to the background?
WorkManager is for guaranteed works that needs to be executed even if your app exit or the device is restarted. From your description this doesn't seem your use case.
A threadpool seems the best option in this case, but you can judge yourself starting from this guide: "Background Tasks".

Categories

Resources