Lets say I have a Service S and Activity A. S downloads data for A (or handles some long running work, whatever), but A is not always present. I don't want S to hang around when it's job queue is empty: S should post the results of the finished works to some kind of a mailbox for A, so A can pull the messages when it comes back again.
Can this be achieved without using SQLite of file storage for the implementation of the mailbox? I'd prefer some faster mechanism, write operations tend to be quite slow on a device. I thought about using a simple static list inside the ApplicationContext, but afaik relying on the ApplicationContext results a risky/fragile solution.
Could anyone recommend a pattern for this problem?
Can this be achieved without using SQLite of file storage for the implementation of the mailbox?
Not reliably. Either it's a file, or it might be nuked before A comes back again. Remember that your process -- where all your static data members and the Application object reside -- does not live forever. Once S shuts down (which is a good thing, thanks!), Android is welcome to terminate the process, taking your "mailbox" with you if it is solely in RAM.
You could persist it to disk yet keep a singleton or something around as a cache, so if A returns quickly you can skip some of the I/O. Or, if it does not really matter much if the messages exist for A, you could keep them in RAM and simply shrug your shoulders if the process gets terminated first.
Related
As far as I can read, Android may kill my process at any time 1.
One might interpret the article [1] such that, at any point, a process must be able to survive a crash. How is that handled? Are there any guarantees of any methods being called if a process is killed this way? The article* doesn't mention it.
My question is, how do you guarantee that a force-killed process resumes in some sane way on next start? The only state my process has (assuming no guarantees are made for methods being called when process is killed) is the state in persistent storage (a DB or elsewhere) and this is likely to be incomplete if process is force-killed.
Concrete example: Let's say I ask a Service to perform some work. This work is not something like playing a music file. It is work that can be considered "done" at some point (e.g. sending data to the web). If my Service gets killed, say after 50% of the work is done, how would my app know if the work was successful? I could have a flag in persistent storage saying "done", but even then, Android might kill my Service after I send the last byte and before I set the flag.
Is there any common way of handling this? My Service could, when restarted, negotiate with the web server to see if the file was transferred, but it quickly gets really complicated and I don't think it would really solve the problem.
[Edit 1 start]
I am aware of the following text [1] but that does not solve the problem for services, "This last state is generated each time the user leaves that part of the application, not when it is killed"
[Edit 1 end]
[Edit 2 start]
I found something really interesting. An apparent inconsistency in the Android documentation related to this. Please see my new question at 2
[Edit 2 end]
[Edit 3 start]
The "apparent inconsistency" has been resolved. It was due to me not being precise about "app"/"process"/"activity" terms. This question still stands, though.
[Edit 3 end]
Are there any guarantees of any methods being called if a process is killed this way?
Nothing is called on your app when your process is terminated.
how do you guarantee that a force-killed process resumes in some sane way on next start?
That cannot be answered in the abstract.
The only state my process has (assuming no guarantees are made for methods being called when process is killed) is the state in persistent storage (a DB or elsewhere) and this is likely to be incomplete if process is force-killed.
You should be updating your local persistent store when the data changes. Hence, your persistent store is likely to be up to date when your process is terminated. An in-memory cache should be treated as a read cache, not a write cache.
It is work that can be considered "done" at some point (e.g. sending data to the web). If my Service gets killed, say after 50% of the work is done, how would my app know if the work was successful?
It would have to negotiate with the Web server to determine what was and was not successfully uploaded.
Is there any common way of handling this?
There are various approaches for trying to maintain "transactional integrity", particularly for long-running operations where process termination poses a greater issue. None of those are unique to Android, as this has been a problem in computers for decades. Most boil down to "check what succeeded, and re-try what didn't". How complicated this is depends entirely on the nature of the data you are trying to update and the available means for updating it.
In an Android library I'm writing, I have a queue which has elements constantly being enqueued and dequeued. A required specification is that no elements in the queue are lost. So, if the application closes, I have to save the queue somehow.
I have two options:
1) Ideally, I could save the queue into a SQLite database when the application closes. However, I'm not sure how to detect this, or even if it's possible. In this manner, I can reload the queue elements which were never dequeued back into the queue the next time the app opens. If someone could tell me how to detect the application close from a library (not an Activity), it would be very helpful.
2) If that's not possible, I could write the queue straight into the database for every insertion and removal. However, this is terribly inefficient, and is too slow for my library.
What's the best way to handle this problem?
So, if the application closes, I have to save the queue somehow.
Update the persistent store when entries are added to the queue. For example, Square's Tape offers a persistent queue implementation.
However, I'm not sure how to detect this, or even if it's possible.
It is not possible. The closest thing that Android has to "application closes" is when the process is terminated, and you are not notified about this in advance.
However, this is terribly inefficient, and is too slow for my library.
It works for Square. Their app has been downloaded millions of times and has a ~4.5 star rating on the Play Store, and it uses Tape.
Note that AFAIK Tape does not write to SQLite, though.
You can't detect when an Application is closed, however this work should be done in a Service in which you can override the onDestroy() method.
In onPause event, do the following
if (isFinishing()) // This tells you the app is closing.
Only in this case save it.
It seems that there is a large amount of information about saving Activity state, but I have been unable to locate much on finding Application state.
I am looking for some design ideas to solve a problem I have run into. I am developing a game that has a fairly large data model (1-2 MBytes). This model exists outside of any Activity, in fact there are numerous activities that all interact with it. These activities are transient, coming and going all the time.
I currently keep a pointer to the data model in my application and all of the activities access the data model through it. I need to save that data model in the event that my application is being killed, but it is far too slow to save it every time an activity hits onPause, which happens very frequently as activities come and go.
What I need is a way to determine that my application (and along with it my data model) are being destroyed. I have searched extensively for this method or callback and have come up empty.
I would appreciate any suggestions.
I have been unable to locate much on finding Application state.
That's because there is no "Application state" in Android, any more than there is in a Web app.
but it is far too slow to save it every time an activity hits onPause
While your entire data model may be "1-2 MBytes", but the amount of data that changes is going to be a small subset of that, for any given change. Use a background thread and only modify the data that has changed.
which happens very frequently as activities come and go
It sounds like perhaps you have too many activities.
What I need is a way to determine that my application (and along with it my data model) are being destroyed
That is not possible. You will never find out that you are being destroyed. Android can and will terminate your process without warning, either at user request (e.g., Force Close, task killer) or for OS reasons (e.g., need the RAM to handle an incoming phone call).
You are welcome to use onUserLeaveHint(), which is called in a number of cases when you entire app loses the foreground, but I certainly would not count on that for something as important as persisting a data model.
I understand why an always-on service is normally an anti-pattern in Android, but my app really seems to be begging for one:
On first load, the app has to go through potentially thousands of small entities from the database to construct the initial state. There's not much data brought into memory (most is lazy loaded later), but that first scan is unavoidable by the nature of the app. This scan can take at worst 6-7 seconds with slow hardware and a big dataset, average is probably around 3. The app is a "impulse use in short bursts" type of thing, so those repeated loads are really not desirable.
I think this begs for a background service to be perpetually alive and holding that state, thus avoiding that load time. It will always be ready to be killed, and not in the foreground, so should the system or user decide that they have it out for the service, no harm is done. But if the service is left in peace, the app will start instantly, and in my case that does a lot for the user experience.
Am I still wrong?
I think this begs for a background service to be perpetually alive and holding that state, thus avoiding that load time.
As the British say, bollocks.
On first load, the app has to go through potentially thousands of small entities from the database to construct the initial state.
Then fix that. Either simplify this work, or persist the initial state in a simpler form for later reuse (e.g., JSON).
If it is OK for you to use a cached result of this work that is held in RAM, it is OK for you to use a cached result of this work that is held in an easier-to-read-in persistent data structure.
An "always-on" service would essentially act like a daemon process and there are plenty of services on Android phones that never turn off.
In this case, though, it seems a better solution would be to simply have a splash screen and/or wait dialog that sits there until the data is loaded. It seems like a bad idea to me to take up resources when the app isn't running just so the app will load faster when the user finally opens it. If the average use of the app is much smaller than the load time, then it would probably be even better to speed up the scan in some way.
People use taskkillers to kill such kind of services. My view is, that when you make the user aware of why your service is running (say, this will load the app quicker), he will understand it and not kill it. You could also ofcourse add an option to use the service or not.
I have a task which I need to run in the background in my Android app. It reads data over the network and populates a database. It can take several minutes to run.
Once it's started, it needs to complete successfully without interruption. (Otherwise I'll end up with a broken half-populated database.) I realise I can never guarantee it will always complete, but I want to make it as hard as possible for the system to kill off this task. For safety I guess I will have it populate a temporary database, and then only swap out the old database for the new one on successful completion of the import.
It's a modal operation; it does not make sense for the user to be interacting with the app while the import is in progress.
My first attempt is using an ASyncTask with a Progress dialog to achieve the modality, but this obviously breaks the "don't interrupt" requirement. I could work around the screen-rotation issue with ASyncTasks, but I don't think that goes far enough.
At the moment I'm not sure if this should be an ASyncTask, a Service, an IntentService, some combination of these, or something else entirely. Can you help me decide?
I'd run it as a service and additionally I'd also have a clean SQLite DB on my server populated with the data the clients are going to retrieve so I can generate a kind of signature. Have the clients check for the correct signature of the DB. If the signature is not matching the servers signature then reinitialize the database filling process.
This is just an idea tho. I have no idea whether it'd be possible with what you are trying to do or not.
You are better off with services in that case. The Android runtime will leave it alone working as long as enough memory is available. In the case it kills the service, you can save the state in a bundle, and the system will restart the process as soon as possible, so you can resume the process, if possible for your solution:
Android Fundamentals, Service Section
Then it is easy to communicate with the service, like showing the progress/ notifications etc, using a handle registry like proposes by Mark Bredy in his Android Service Prototype