views:

437

answers:

2

Here's scenario:

  1. I have 2 activities and one service
  2. First activity is a landing view/search page. Second activity displays search results
  3. Search is always executed against internal SQLite db
  4. Periodically (say daily) db needs to be updated from the remote source which is a long process
  5. If user performs the search during the update I want to wait until the update is over while displaying "Please wait" progress alert. I don't want to query and display search results until refresh is fully done.
  6. The db update is triggered by AlarmManager and executed by service which puts "UPDATING" status into db while update is in progress
  7. I can easily query the status but how do I wait and periodically re-query the database? I'm using AsyncTask to process search results and my knee-jerk reaction was to put a loop with wait() into AsyncTask#doInBackground method but that's dangerous and simply doesn't work since I'm not controlling the UI thread so I end up with IllegalMonitorStateException.

What would be a "right" way to properly wait (may be even with status update) in this case?

P.S. I placed the "wait" code into a Runnable and executing it even before I get to my AsyncTask. It works e.g. Thread.sleep(2000) still I'm not sure that's the way to do it safely. Does anyone have any experience with FutureTask?

+1  A: 

If user performs the search during the update I want to wait until the update is over while displaying "Please wait" progress alert. I don't want to query and display search results until refresh is fully done.

That's your call, but bear in mind you are creating your own problem. Personally, I'd dump this requirement. The user should not be inconvenienced just because an alarm went off.

For example, you could disable the alarm and re-enable it when the activity goes away.

Or, have the update be performed in such a way that it is atomic (e.g., do the update on a copy of the table, then sync the tables in a transaction), so that the activity can still safely access the database while the update is occurring.

What would be a "right" way to properly wait (may be even with status update) in this case?

Have the service tell the activity when the update is done, via some sort of callback, or perhaps a broadcast Intent. Keep the progress indicator alive until this occurs. This still introduces some timing challenges, which is why I'd just dump the requirement.

CommonsWare
I was thinking along the same lines. I can't postpone the update since user can start activity while update is already in progress. So I'll do the flip (that's the different question) feeding user staled data until the update is done. However there are two issues that remain: 1. What about the initial update after user installs the app for the first time? There's nothing to flip at this point since I don't have old data yet 2. I provide user with ability to force the refresh. At that point I need to show user that update is in progress
DroidIn.net
"There's nothing to flip at this point since I don't have old data yet" :: shrug :: see if the table is empty and skip the flip, or something. "At that point I need to show user that update is in progress" -- use the technique in the last paragraph of my answer. The timing issue falls away (I think) because the user initiates the refresh.
CommonsWare
Skip the flip is obvious... Actually I just came up with idea that instead of making user wait while looking at progress dialog I can incrementally add to the ListView (while displaying updating indicator say at the bottom). The service can communicate updates back to the view
DroidIn.net
That works. I have an example of using an AsyncTask for that (albeit not from a service to an activity) in one of my books.
CommonsWare
A: 

Thanks to Mark (as always) for useful insights. Here, I'm going to outline how (in my mind) the above scenario should be done:

  1. Instead of poking the database simply bind to the service and start waiting
  2. If you can't bind to the service then it's not running so there's no need to monkey with it - just query the db and do what you need
  3. When service is up, start waiting and process any feedback that service sends back. These can be interim updates and then final indicator that service is done
DroidIn.net
You really don't even need to bind to the service. If this is all your app, you can have a static containing a pointer to the service, which is set in Service.onCreate() and cleared in Service.onDestroy(). Then when your activity wants to interact with that service, look at the static and use it if non-null. From there on out it's just plain Java.
hackbod
Actually - I coded with anonymous BroadcastReceiver since I don't have to communicate from Activity to Service
DroidIn.net