Good Design: Worker Threads and Activity Restarts - android

It is well known that Android places our application components (Activities, Services) under threat of being killed at any moment. This makes things really complicated if you want to provide a robust, non-leaky solution, while at the same time keeping the code clean and addressing separation of concerns.
Problem:
An activity starts a time-consuming task (in form of a Runnable, AsyncTask or whichever other mechanism). A progress dialog should be displayed. The task will open several connections, but it should have a means of updating a progress bar, and it might need to display a choice dialog half-way to completion. It also should be able to close the dialog and show a Toast when an error occurs, or when it completes.
When an Activity is killed and later a new instance is recreated, I can think of two options:
Try to kill the running task as well, and spawn another task instance when a new substitute activity instance is created. This is not an option for a long running task, as it is unacceptable from a user point of view to start the task and see the progress bar indicator going from 80% back to 0% with no apparent reason. This however is the approach followed in the Shelves example by Romain Guy. Apparently this is the approach recommended in the official Developer Guide.
Keep the task running: So we could have an activity subscribing to (and possibly starting) the task for updates at onResume, and unsubscribing (and possibly pausing) it at onPause.
Following the second option means that we should prevent the task of being collected in case the activity is temporarily destroyed. For instance, we could declare it inside a custom Application class, or even offer it as a service to the rest of the application (using a Singleton? an Android Service?) I'm no fan of this, though, since the task is used only by the activity, so it doesn't make sense to make it available to other classes. Android Services on the other hand also have to deal with life cycle.
Option #2 also involves ensuring the old activity is not leaked. We should not try to update the GUI if no activity is present. The whole think should be thread-safe. Finally the task should not remain in memory after we are sure it is no longer needed. But at the same time, if the initial activity is destroyed, the task keeps running, and a new substitute activity kicks in, the task has to be around to update the GUI immediately and display the task current status (or result in case it completed).
The fact that the task might be running when no activity is shown is another problem to deal with, since we could need user interaction (choice dialog) synchronously at some point. This could be solved if the activity had the ability to immediately pause the task when reaching onPause, but the task could take some time to pause itself or even be unable to do so.
I know similar questions has been asked before, but I'm not asking specifically about how to solve the problem, but about what design would be recommended in order to achieve loose coupling and isolate the activity from the task as much as possible.
In short:
- This is a recurring problem in Android development and someone has probably come up with a clever design before. Is there a design pattern, or a well tested library that simplifies this?
- If you were to implement #2, which class would you choose to for the task (Runnable, Service, etc) and how would it communicate with the activity?
P.S. Please refrain from posting solutions based on the "orientation | keyboardHidden" hack XD. Other than this, any help would be appreciated.
UPDATE:
My first attempt has got a bit messy. The app is a bluetooth printing utility, which involves Detecting BT status (and asking the user to enable it if it were disabled), retrieving paired devices (and asking the user to pick one if there were more than one), then sending the data and finally inform the user about the result. As this task was 80% IO code, 20% GUI-related operations, I had to group the GUI code at the beginning and end of the task, then moving it out of the task back to the activity. I have an IntentService that does the heavy lifting, then reports back to the activity via BroadcastReceiver. This solves most of the problems but there are a few issues with such a design. First, there are all those constant strings used as keys to put and retrieve fields from the input and output Intents, which are introducing semantic coupling. I'd have preferred type-safety in the activity<->service communication. And I yet have to solve how to pass complex custom objects to the service in the Intent, by now I'm stuck with Parcelables and primitive parameters. Finally, both the activity and the service are using the same API (bluetooth), I'd have preferred to have al the BT-related code in a single class. I think I'll throw away this design, and try again with a custom printing queue based on threads, despite it will make harder dealing with killed activities.

why not have your activity start a service to host the long running task? a service is your best-bet for keeping things running long term. you can provide hints to the OS to leave it running. see startForeground() and START_STICKY.
you can communicate back to the activity by broadcasting from the service. have your activity programmatically register a broadcast receiver to listen for those intents. if the activity is paused, unregister your receiver, that way you won't respond when when you aren't in the foreground.
if the OS destroys your service, then it's killing the process, so your task is doomed anyway. the best you can do is restart it. anyway, this won't happen except in the most extreme conditions if you consider the above.
EDIT: summarizing some thoughts captured in the comments ... keeping a long running worker processe and automatically restarting it is almost always the wrong thing to do. mobile devices do not have the power source to make this feasible. the author stated this he has a special condition that negates this concern ... that would only be true if the device is connected to a power source almost always.
the android model is to provide a robust set of intents (notifications) to which your application can listen. these intents wake up your device / application when it's time to do something. your app should handle the notification, and then allow the device to go back to sleep immediately.
if there's no stock intent that maps to your event, you can use Google Cloud Messaging to wake up devices from a server. in other words, let the long-running processes run on the server, and wake up the device when required.
if you can do that, an alternative would be to use AlarmManager to wake up periodically and check some state. even if you did this frequently (e.g., every 5 minutes, which is also a no-no), it'd be much better than keeping the device awake always as is being suggested. also, make use of inexact wakeup intervals (see documentation linked above).

Related

Where is the best place to start a long running, application-wide, background task

I have a long running background task that I would like to start when the app launches and shutdown when the application shuts down. I'm already quite aware of the activity life cycle and what gets called when an activity gets created and destroyed.
I'm coming from an iOS background, and over there we have some calls that are made during application startup and shutdown. Is there something similar in the android world? I've searched a lot and all I'm finding are answers relating to an activity, not the entire application.
(Android is relatively new to me, so I may just not know the correct terminology to search for.)
EDIT:
I'll try an be a bit more specific. I have a background task that needs to be continuously running while the user is using the application. It will be streaming data from a server continuously while the application is active. It does not need to run when the application is in the background. It doesn't seem to make sense to me to tie the startup / shutdown of this background process to any one single activity since it may not be the same one activity that starts up when the application becomes active.
I am (possibly mistakenly) assuming that the OS takes care of starting / stopping background threads when the application resumes and pauses. If that is, in fact, the case, then all I really need to do is spin up the background task when the application first launches, i.e. when it is loaded into memory and becomes active for the first time that session.
It doesn't seem to make sense to me to tie the startup / shutdown of this background task to any one single activity since it may not be the same one activity that starts up when the application becomes active.
That's reasonable. It is somewhat difficult to implement, though.
I am (possibly mistakenly) assuming that the OS takes care of starting / stopping background threads when the application resumes and pauses.
You have it exactly backwards. Android pays not one whit of attention to any threads that you fork yourself, directly or via thin wrappers like AsyncTask.
In addition to that point of confusion, you appear to be equating "user switching to another app" with "app shutdown". Those may be the same thing in single-tasking operating systems. They are not the same thing in Windows, OS X, Linux, Android, etc.
So, what you seem to be seeking is having a background thread running doing this streaming work while your UI is in the foreground, and then stop when your UI is in the background. The problem is that there really isn't a straightforward way of accomplishing that in Android.
One close approximation would be to create and register a custom Application class, where you override onTrimMemory(), and stop your background work when you get to TRIM_MEMORY_UI_HIDDEN, TRIM_MEMORY_BACKGROUND, TRIM_MEMORY_MODERATE, or TRIM_MEMORY_COMPLETE -- whichever of those that you encounter first. If, when one of those arrives, you determine that your streaming thread is still outstanding, shut it down.
In terms of startup, you could use onCreate() on that same Application singleton. The problem is that this will be called on any process creation, which may include scenarios in which you do not have UI (e.g., you are responding to some system broadcast, like ACTION_BOOT_COMPLETED), or possibly your process is going to parts of your UI that do not depend on the streaming. If you have none of those scenarios, then onCreate() in Application would be fine. Otherwise, kick off the streaming in onCreate() of whatever activities need it.
While normally we manage long-running threads with a Service, that is for cases where we explicitly want the thread to continue after our UI is in the background. Since you do not want that, you could skip the service.
It depends on what you want to do exactly. When you're just interested in the app starting for the first time you could #Override onCreate().
Or maybe you want to use onResume() as this will get called whenever a user brings the app to the foreground.
But this really depends on what exactly your background task is doing and what you want to do with it, to get an exact answer you need to provide more details.
Here is an overview for the actiity life cycle that should help you:
You can extend the default Application class and implement it's onCreate() method to detect when the app is launched. There is no corresponding method for when the app gets closed though.
Do not forget to specify it in the Manifest file.
In Android the application isn't shut down unless the system runs low on memory. You won't get a warning about that, it will just call your Service's onDestroy lifecycle method. If you want to do it when the Activity is visible on screen, use onStart and onStop. If you want to do it when the Activity is resident in memory, use onCreate and onDestroy.

Activity destroyed after 1 hour

I'm new to Android development. I'v developed an android application which needs to store the connection/data even after 1 hour. Currently I have all the data and the connections(chromecast mediaplayer) in a singleton class. But, when the user puts the app into the background for about an hour, the activity is destroyed so the connections,data etc are lost causing my app to crash when re-launched.
I've read up on the android services, Can I use these services to hold the singletons so even when the activities are destroyed I can have data binded back to the views when re-launched?
Or is there a way to make sure that the activities are not destroyed when android decides to do a cleanup?
Please advise
Thanks.
I think you might misunderstand what an Android application is.
Your application is a bunch of components that run in a single Linux process. Components come and go, within that process. You have absolutely no control over the lifecycle of the process itself.
The answer to part of your question is that "yes" a Service will stick around after an invisible activity is destroyed.
When an Activity becomes invisible, it gets destroyed. If your process is not doing anything else, then the process is likely to be killed too.
If your process is also running a Service, it is less likely that it will be killed. It is just less likely, though. The process will eventually get killed. When it does, your singletons will be gone. There is nothing you can do to prevent that. So the answer to the second part of your question is "no". You cannot depend on singletons in your service to be around when the process is relaunched
You might look into using the Application object. Its lifecycle is roughly the same as that of your process. It will not live forever but it will be around whenever any other component of your application is around (except ContentProviders).
It sounds like you want to keep connectivity to a chromecast device around when your application is in the background. Obviously services can be helpful but I have a few comments that may come handy:
Services can be killed by system but based on how you have set them up (e.g. the return value of onStartCommand()), they can be restarted by the system. When that happens, you cannot expect that your dynamic data is still there (for example your singleton). You need to include logic to recreate what you need again (for example, rebuild your singleton)
Phone can go to sleep when left for a little while (or user can lock his/her phone), so when phone goes to sleep, wifi may drop after a little while, based on the phone settings and the build on your phone; some do this more aggressively and some not (even if you hold a lock, it can still happen). The point is that you have to assume that it may happen. Even if you have a service, your Cast connection will go down due to wifi loss, so the proper way to handle things is not to try to keep the connection up all the time (since you can't) but is to have logic to re-establish connection when circumstances is right. In order to do that, you need to preserve enough information to be able to bring things to the state that they were. Your logic should also be intelligent enough not to reconnect if it shouldn't.
Android O.S can destroy any activity , when it is low at resources it destroys any activities to make more space for other apps.
But you can use background service to hold your singleton
You can use this link to create your background service

Why would one use AsyncTasks when you can use Services?

When you learn to develop for Android, you learn that if there's any type of process -that takes more that 5 seconds- running inside the ui thread, the system will show the infamous ANR message. That's when they introduce the evil monster... AsyncTask. At first you see this class as your savior, but then you realize it's the biggest problem in development... Handling rotation or ui events becomes so problematic, it's even painful.
What do you guys think about it... are AsyncTasks worth the trouble? Any other method that makes it less of a problem to perform long running tasks regardless of screen rotation?
I guess you'll probably say, hey why don't you just make it a Service... Yeah that's a solution, but then let's go to my real question. Why don't we just use services and stop using AsyncTasks completely... Are they useful at all?
While developing it is always good to choose the suitable component for the execution. You need to clear your requirement and according to it you can choose between a Service or an AsyncTask.
A service is an Android component that lives independently from any other components. Activities may come and go, but services can stick around, if you wish. They serve a number of roles, from managing state that multiple activities rely upon to serving as cron jobs to handling longer app widget updates.
AsyncTask is a class that arranges to do some work off the main application thread. From the implementer and user of the AsyncTask, how it performs that bit of magic is not generally important. In reality, it uses a thread pool and work queue. AsyncTask uses a Handler to help arrange for select bits of work to be done on the main application thread (e.g., notifying the user of progress updates).
For more clarification see the difference between them.
AsyncTasks and Services are not peers. Either might invoke the other, their purposes are different. I have just a few things to add to Lucifer's answer.
A key thing to remember is that if your process is in the background, hosts a Service, and is killed, that Service will be reincarnated at some point. This is not the case for an AsyncTask that may be running in a process when it is killed. This means that work being done in a Service is guaranteed to make forward progress in the future (although its not guaranteed when). If you have work that must be done, better to do it in a Service.
Calls to Service.onStart and Service.onStartCommand happen on the process' main thread. If you block this for a long time, you'll get the same "app not responding" dialog. As a result, an AsyncTask can be a great thing to use inside a Service to do the real work.
I tend to use AsyncTasks initiated from an Activity for things related to the UI. These are often things that aren't required work and whose work can be easily redone in the future. For example, this might be reading a bunch of images off disk or make a calling to a web service to see if the user has any notifications. This work can be done again, and if the UI is destroyed because the process is killed the work probably needs to be done again anyway.
If on the other hand you want to say upload a group of photos to the web as soon as possible, I'd farm this out to a Service which will use one or more AsyncTasks to do it. This is work I want to get done and want to be resumed if it gets interrupted. If you're going to use a Service, consider using an IntentService so that your Service stops when its work is done.

AsyncTask executed from Service (IntentService) and Activity - is there a difference?

Is there any difference between AsyncSync being fired up from Activity or IntentService?
I'm building an app which downloads and uploads files via http. I use custom notification layout with progress bar for each transfer.
I choose between doing transfers in parallel or putting them into queue (which option would you recommend?).
For the option with queue I use an IntentService, so Android framework takes care of putting tasks into a queue for me.
For having them in parallel I use AsyncTasks. But I fire them up from IntentService (could be Service as well) - is there any point doing so? IntentService is terminated right after it executes AsyncTask, so the AsyncTask runs without any "parent".
What if I fired up those AsyncTask from Activity, go to the homescreen and system decided to close this Activity? Can it do that? Will the AsyncTask survive it?
What would be the preferred approach for this case?
AsyncTask isn't really appropriate for things where you're concerned about surviving outside the lifecycle of a component (if you need to message back to that component). If you're going to the extent of having a service, I wouldn't even bother with AsyncTasks. As far as parallel or queue, that really depends on a lot of different variables, but I certainly wouldn't make it completely parallel for any number of downloads/uploads. I'd set some limit on the max number of concurrent transfers.
There's no point in using IntentService for parallel exeuction--you've noted the problem with that already. You're getting into territory which is not really covered by the Android API. AsyncTask and IntentService are nice abstractions that make several scenarios easy, but parallel execution of many many tasks is not one of them. It's probably best to use some of the Java threading/concurrency classes. Take a look at ThreadPoolExecutor.
First your easy questions, an AsyncTask will survive being in the background as long as the parent application is not killed. Starting it from an Activity will tell the system that it can be killed if memory pressure requires it. The system also does not consider it having a long running process that may be interrupted.
The dev guide Services has a great info box under its "The Basics" heading about whether you should use a Service or Activity. Activities get none of the memory pressure consideration that Services get or a restart after a kill, when system resources become available again.
If you wanted me to make the call for you, use the IntentService. Whether to run the downloads in parallel or series or some combination is a tough call because you must consider the network (Wifi or Cellular), file size, and other system resources.

Should I do work in a separate thread if my android app has no UI?

My application is essentially a service that is started on boot (by a boot-completed receiver), and should periodically gather data, send it over the network, and go to sleep (probably using AlarmManager). It has no activities - no UI whatsoever.
Is there any reason to spawn an additional thread to perform the application logic?
if not, where should I perform the logic? in the OnStart method?
Is there any reason to spawn an additional thread to perform the application logic?
Absolutely. Your service will be killed off if it fails to respond within 5-10 seconds. Nothing can tie up the main application thread for that length of time.
I recommend an IntentService for use with AlarmManager. In particular, if you want the device to stay awake while you are doing whatever it is you are doing, you might consider my WakefulIntentService.
Also, regarding "no UI whatsoever", if you plan on distributing this app via the Android Market, please bear in mind that users seem to dislike applications with no UI. They install it, get confused when there is no icon in the launcher, and give you a one-star rating. Even if you do not need a UI for actual operation, you might consider at least having some activity in the launcher, that shows documentation, perhaps a log of work being done, allows adjustment to the frequency of your work, etc.

Categories

Resources