views:

51

answers:

1

I've built an application which uses an IntentService for manipulating the application's database and calling web services.

Example:

I have an Activity which is a list of items. This list is populated by a CursorAdapter. Each item has a checkbox and the list is ordered by checked (unchecked items on top and checked items on bottom). When an item's checkbox is checked, I send an Intent to my IntentService telling it to flag the checked column in the database table for that item, then a message is Broadcasted, the Activity sees it and re-queries the Cursor.

When I'm using a mobile device and start rapidly checking checkboxes, the app essentially hangs. The UI doesn't hang, but the IntentService in the background does. I can tell because the Cursor on screen isn't getting refreshed.

It is my understanding that Intents passed to IntentServices are not processed asynchronously. Is that correct? It's behaving almost like I need to be using semaphores, but I can't understand why if Intents are queued and processed individually.

Suppose I check 3 checkboxes; so there are 3 Intents queued. After the first one I broadcast a message, which the Activity receives, telling it to requery it's cursor. Could a potential problem be because I'm requerying the Cursor at the same time I'm processing the second Intent (updating another row in the table)? Querying the Cursor happens on the UI thread.

^ I think this is my problem. What should I use to synchronize this? Semaphores? Can someone point me to some documentation/examples?

Another potential issue is what if my Activity manages a Cursor on a table; also my IntentService queries a Cursor from the same table. I have instances where this happens as my IntentService is going through each row in the Cursor to find items to send to the web service. Though I wouldn't think this would be a problem so long as the Cursors don't query at the same time?

Does anyone have any other ideas?

+1  A: 

When an item's checkbox is checked, I send an Intent to my IntentService telling it to flag the checked column in the database table for that item, then a message is Broadcasted, the Activity sees it and re-queries the Cursor.

This has a "using a Buick to swat a fly" feel to it.

It is my understanding that Intents passed to IntentServices are not processed asynchronously. Is that correct?

onHandleIntent() in your IntentService is called on a background thread, and startService() is always asynchronous.

It's behaving almost like I need to be using semaphores, but I can't understand why if Intents are queued and processed individually.

Individual Intents are queued and processed serially by the IntentService.

Could a potential problem be because I'm requerying the Cursor at the same time I'm processing the second Intent (updating another row in the table)?

This certainly does not help.

What should I use to synchronize this? Semaphores?

I'd just use synchronized(someStaticDataMember), but semaphores should work.

Though I wouldn't think this would be a problem so long as the Cursors don't query at the same time?

The actual query is not executed until the Cursor is touched (e.g., moveToFirst(), getCount()). After that, so long as the query result set is under 1MB, the results are entirely in the Cursor.

I agree in part with Christopher: use some logging to figure out exactly what is going on, and consider making this a bit more lightweight (e.g., AsyncTask for the database write rather than an IntentService).

CommonsWare
What if I spawn a thread to write to the database, but the Activity gets reclaimed before the thread finishes? I understand the likelihood is remote as writing is a fast operation, but say I'm writing an Array of objects. That data is tied to the Activity and, if the Activity is reclaimed, my thread is in trouble.Can you point me to an example of synchronizing a static member? I've done this on singleton constructors, but I'm not quite sure how to wrap it around two separate blocks of code.
Andrew
I should also note that my database inserts/updates/deletes are in an IntentService because of a video I watched. There's a Google I/O 2010 presentation video where a Google engineer talks about models for building web-service heavy applications. He asserts that long-running operatings, including database calls, should happen in a Service.
Andrew
http://www.youtube.com/watch?v=xHXn3Kg2IQE
Andrew
@Andrew: "if the Activity is reclaimed, my thread is in trouble" -- why? The `AsyncTask` will run to completion. "Can you point me to an example of synchronizing a static member?" -- not really, it's trivial Java. If you are capable of writing an `IntentService`, using the `synchronized()` keyword should be a walk in the park! "He asserts that long-running operatings, including database calls, should happen in a Service." -- I was there. `IntentService` is not the only pattern for doing work off the main application thread, and that Googler and I simply disagree on some things.
CommonsWare
@Andrew: And I am not saying you have to switch away from using an `IntentService`, just that I would not use an `IntentService` for something as trivial as a simple database operation.
CommonsWare
Suppose I have member variables belonging to the Activity which store some pieces of data. I want to write those pieces to the database. I launch an asynctask to write them and, while the asynctask is executing, the Activity is reclaimed by the system or destroyed (screen rotation). Wouldn't this cause a problem?
Andrew
@Andrew: Do not access the activity from the background thread. Only access it from the main application thread (e.g., `onPostExecute()`). Use a `static` `AsyncTask`, and pass it from old to new activity on a configuration change (see http://github.com/commonsguy/cw-android/tree/master/Rotation/RotationAsync/). Everything should then be fine, on both a rotation and a BACK button.
CommonsWare
How am I not to access data from the main application when that's where the data lives and that is what I need saved? Or are you suggesting copying that data to variables in the AsyncTask and the AsyncTask operation act on those variables?
Andrew
@Andrew: "How am I not to access data from the main application when that's where the data lives and that is what I need saved?" -- `Activity` is a class, not an application. "Or are you suggesting copying that data to variables in the AsyncTask and the AsyncTask operation act on those variables?" -- yes, copy the data into the `AsyncTask`, just like you would be copying the data into the `Intent` used with your `IntentService`.
CommonsWare
I'm sorry. I often still read Activity when I see Application. Should I really be making this AsyncTask static? Suppose the user performs several actions, each requiring a write to the database. Suppose one of those actions is invoked before the resulting operation from a previous action has been completed by the AsyncTask?
Andrew
@Andrew: That's a completely separate problem, one you will encounter regardless of how you do your database operations (`AsyncTask`, `IntentService`, etc.). A typical approach is to block user input that might affect the database while a database I/O is going on. It is one of the reasons why you may be doing a bit of premature optimization. Only by testing your app will you be able to determine if you truly need to do some or all of the database writes in the background, which adds all this headache. Be sure to test on hardware (flash I/O differs from hard disk I/O).
CommonsWare
But blocking user input during database I/O is not really acceptable, is it? At that point you might as well run everything on the main thread. Doesn't an IntentService queue Intents to be processed once the proceeding Intent has finished processing? (I know I've asked this question before, sorry, just want to be sure). At any rate, if database I/O operations are building up, wouldn't it be appropriate then to shove that piece into a Service? I'm not opposed to using AsyncTask. I just want to use what is ultimately going to work with many frequent database calls.
Andrew
@Andrew: "But blocking user input during database I/O is not really acceptable, is it?" -- most of the time, database I/O is on the order of a few milliseconds. Again, that's where testing comes into play. "Doesn't an IntentService queue Intents to be processed once the proceeding Intent has finished processing?" -- yes. "At any rate, if database I/O operations are building up, wouldn't it be appropriate then to shove that piece into a Service?" -- it's a possibility, but it's not an absolute requirement.
CommonsWare
Ok. I'll see what I can put together with this information. Thank you for all your help! (with this question and others)
Andrew