views:

137

answers:

3

I need my Android app to save it's state to disk when its activity is put in the background or killed. It's been suggested that I start a thread when onPause() is called and perform any expensive I/O procedures there (see http://stackoverflow.com/questions/3894668/saving-loading-document-state-quickly-and-robustly-for-image-editor).

In what situations will the OS kill the thread and how commonly do these situations occur?

I assume it will be like how Activities are dealt with where the OS can arbitrary decide to kill the thread but will mostly only do this when resources are extremely limited. It would be nice to find some specific documentation of this though.

From playing around, with some test code, a background thread started in onPause() will run indefinitely in the background on my device (I tried loading lots of apps and couldn't get it to be killed).

For my specific app, I'm writing a bitmap editor where I'm using the Command pattern and the Memento pattern to allow undo and redo of edits. I'd like the user to be able to undo/redo their edits even e.g. the user gets a phone call and the activity is killed when it is put in the background. The best solution I can think of is to use a background thread to constantly save my command and memento objects to disk during application use and to finish up saving any objects that are left in a background thread if onPause is called. In the worse case, if the thread is killed I'll only lose some edits.

A: 

Normally, saving your state in onPause is the right thing to do if it's quick. I don't think it's clearly documented when a process is killed, but you sometimes see it in logcat when you run some demanding apps (say, after running Google Earth and Browser).

There's also an option in the Android DevTools to automatically destroy activities as you navigate away from them, although that probably doesn't extend to the process. (DevTools are on the emulator, and on some rooted phones).

I think your approach sound reasonable - use a low-priority thread to constantly update the save data, and give it normal priority in onPause, and set a flag in onPause that tells it to terminate after it finishes.

Obviously, you'll need to make sure you don't run into synchronization issues if you get to onResume immediately after onPause (i.e. while the thread is still busy saving).

EboMike
+1  A: 

In what situations will the OS kill the thread and how commonly do these situations occur?

The OS will not kill the thread, unless it is killing the process -- Android does not do anything with threads you create yourself. If you are the foreground process, you will not be killed. The odds of Android killing the process within a few seconds of you losing the foreground (after onPause()) are miniscule. The documentation on process lifetime -- what there is of it -- can be found here.

CommonsWare
Just to clarify, if you create a thread in the process that is attached to some activity, this thread will be killed when the activity is killed (i.e. the process and the thread are linked)?
memcom
@tifftuff: An activity is not an application. An activity is a component of an application. An application may have many components, including multiple activities, services, etc. When the last component of an application is destroyed (e.g., user presses BACK from the one-and-only activity in a small application), the process is earmarked for recycling or termination. DO NOT LEAK THREADS. If you start the thread, you must arrange for it to terminate, as Android will not terminate it for you, other than by terminating the process, which may not occur for some weeks.
CommonsWare
Thanks. Do you have any recommendations about whether a Service would be more appropriate to use than a thread for this background work?
memcom
@memcom: Threads and services are orthogonal -- the choice of one does not eliminate the choice of the other. Services are automatically in the background...in terms of UI. From a threading standpoint, they run on the same main application thread that your activities use. Hence, time taken in a service will tie up the UI, possibly causing an application-not-responding (ANR) exception. Hence, I expect that many services will be using `AsyncTask` or their own thread management. In other words, it's not "service or thread", it's "service *and* thread".
CommonsWare
Thanks. To clarify, what I meant was, should I really be using a Service (which starts its own AsyncTask) to do my background work or is it OK to just create an AsyncTask directly in my onPause() function to finish the work I need done? I'm just wondering if there's any difference as implementing a Service is a little more involving.
memcom
@memcom: Well, as always, it depends. :-) If the task is "fire and forget", receiving data in its constructor and nevermore needing to touch the activity, starting the `AsyncTask` from the activity should be fine. If the `AsyncTask` needs to use the activity, though, things get a bit messy, because the activity may go away (e.g., orientation change). There are patterns for dealing with this, but a service may be another option for you. The best answer may be an `IntentService`, which effectively combines a `Service` with an `AsyncTask`, designed for commands to be run on a background thread.
CommonsWare
A: 

Your thread may be killed at any time after the activity is destroyed, or it may never be killed. Depending upon such a thread is very bad form -- you could end up with a half-completed operation, or with a thread that sticks around forever.

If you wish to perform a background operation that continues even when there is no foreground activity, you almost always want to run it inside a Service. On the other hand, the service is less likely to be killed, but there's no guarantee unless you use "startForeground". This will end up displaying a notification to the user that something is happening in the background, but as far as I know it's the only way of running an asynchronous background thread that is guaranteed not to be killed.

Honestly, the right answer is to make sure that there is never any temporary process state that will take a long time to save. If you are having to write a large file to reflect a few user changes, consider maintaining a "transaction log" which you can use to create a restartable save operation. Given this, you can safely run your saves in a service and know that even if it gets killed, it will be automatically restarted when resources become available.

beekeeper
The thread will terminate within about 5 seconds as I wait to confirm my data was written to disk (about 2Mb worth). My autosave loader will take into account that the thread might be killed before finishing and the user might lose 30 seconds of work. Do you not think starting up a service for this is a little heavy weight? I could create a transaction log of edit operations but these also need to be saved to disk on exit (where else can I quickly store it?) and the user could make a big edit and then get interrupted.
memcom
Starting up a new service is absolutely *not* too heavyweight. This is the recommended way to do things when, for example, processing a Widget notification if it's going to take more than a second or so. Services provide *exactly* what you are looking for -- the system will realize that they have a good reason to stick around and will try to avoid pre-emptively killing them, so you can be much more confident that you'll get your 5 seconds of background processing. It sounds like you are already well set for the rare case where the service needs to be killed, so that's all you need.
beekeeper