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.
Related
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.
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.
I've read by many people to not use AsyncTask for web requests, but rather use IntentService, since AsyncTask is bound the lifecycle of its activity. is that always the case? cause I'm making a social media app, And I wonder if each action the user makes should launch a service (for example, press like, add comment, etc)?
Do not use AsyncTask, as you may have read, it is problematic on Activity recreate. However, you may use AsyncTaskLoader with LoaderManager, which is designed to fix the problem.
And there is one point you are wrong with IntentService, you do not launch a service everytime using IntentService. You are submitting requests to the same service only (unless it is being killed), and the IntentService pull requests from the stack.
I tend to use a mixture of both. I use an IntentService when requesting/posting data from a server, then deserialising it and storing it locally. All this could take several seconds depending on the amount of data returned and the server's response time.
I would use an AsyncTask within an activity if I know that the request is going to be very quick. I use it for more fire and forget small tasks.
The important thing to bear in mind is that your AsyncTask is tied to your activity. And if your activity gets destroyed (due to an orientation change etc.) then you could easily have an NPE in the onPostExecute().
If the task you wish to perform is specific to a Context and is considered garbage once you leave the Context, such as if you consider the user session to be invalid once they leave, then an AsyncTask is a good choice.
However, if the task is specific to a context and may take a long time to perform and you want the user to be able to leave and come back later to see the result, a service may be well suited even though the task is specific to a Context.
If the task you wish to perform is applicable outside the scope of a Context, then a service may be better suitable.
If you just always use a service, you'll probably be over complicating your code.
I'm using Fragments and LoaderManager. I have to launch an unknown number of tasks, and they might be run in parallel (otherwise I'd just reuse one and only one loader). For example, I have a listview, and each row might have a button to save the content of that row to a webserver. The user could initiate a save request on multiple items in parallel.
private int nextId = 0;
private void onClickListener() {
Bundle bundle = new Bundle();
bundle.putNextData(...);
getLoaderManager().initLoader(nextId++, bundle, this);
}
I could try bookkeeping myself, so create a pool of loaders manually and reuse them when possible, seems like it might be something already implemented by the API?
Thanks
I don't think you should use a Loader for saving data to a remote server.
Instead, use an IntentService or something similar to process a queue of "save" operations. This way, your communication with the web server can be batched, collapsed (i.e. multiple queued saves for a single item can be collapsed into one operation), and will live beyond the lifespan of your activity if need be.
A save queue processed by an IntentService (or equivalent) is also a great way to retry failed operations with backoff, since you can implement delayed retries with exponential backoff using AlarmManager.
An IntentService or bound service are always good approaches for that.
As Roman points, note that enqueuing several requests and called them separately is not highly recommended (it is very likely that you give a lot of work to the radio connection - when using data - which among other things drain your battery. Here is must-read about that)
I'd personally recommend to use a bound service with a queue of requests and a pool of threads available (that approach gives you full control for more complex network operations like in your case). There are more details on the approach here and a testcase working example over here.
Update us about your progress.
You are at the right direction, let me just help you a bit.
Reusing is indeed a good idea, and you do not have to worry about it because Android did it for you(Or Java actually ;)
It called ThreadPoolExecuter, you can start as many tasks as you wish and he will only open the predefined number of threads.(Best practice is trying to open as many threads as parallel network connection can be run on the device. From my research it is between 4 - 9).
And if you are trying to download same URL twice may be you can protect your self and open only one task for it.
Here's scenario:
Client makes remote call to the service (returns void) and provides
a callback object
Service executes some long running logic on the background thread
and then uses callback object to trigger ether success or failure
which (since these manipulate visual elements) execute in
Activity#runOnUiThread block
The scenario runs fine. The question is - can I use AsyncTask to make
code less verbose (how?) and would be there any advantages in doing it
that way?
Or should I just get away from client callbacks alltogether and
execute remote service calls retrofitted to return some value within
AsyncTask#doInBackground?
It is difficult to say whether AsyncTask will make things less verbose, since we don't know the verbosity of your current implementation.
For me, AsyncTask means I don't have to worry about cleaning up threads myself (e.g., post some sort of kill job to a LinkedBlockingQueue my background thread is waiting on). It also eliminates the custom Job classes I used to create for using with LinkedBlockingQueues. And, it simplifies a bit doing final work back on the UI thread.
In your case, with a remote service, the UI thread issue is less critical, since the activity needs to handle that itself.
I don't see what the difference is between your #2 and your last paragraph. In both cases, your service will call the callback object, which will use something like runOnUiThread() to arrange for the work to be done on the UI thread.
AFAIK, the only two ways to have a service doing any sort of asynchronous work let the client know that work is done is by a broadcast Intent or a callback object. Broadcast Intents are convenient but public (i.e., other code can watch for them).
I suspect I probably have not helped much here, but I just don't know enough of your scenario to provide greater detail.
I'm having quite the same question : i'm developping a map activity, with a 'lazy-loading' functionnality (xml from Network, parsing it, then updating my map with the 'items' created from that parsing...)
i wondered what would be 'the best' way to implement it...
async service launched from a thread, an update notification via Intent?
just a thread (no service, since i don't need to expose it to other applications) w/ callback
asyncTask with callback
i'm comparingthese in terms of speed, using the Android SDK performance analysis Tool traceview
I guess a more precise answer might be found from Android contributors on the Android-developper-group...