May 20, 2009

A Background Task Class

I am working on an app that makes XML calls to a web service. At first, I made the calls within the main thread and waited for the results to come back. Not surprising, when these calls took longer than usual, the user interface suffered. Basically, the UI locks up while waiting for a response from the web service. This is because the main thread is busy and cannot service UI-related events, such as touch events. What I had to do was make the web service calls in a separate thread so that the main thread can continue to service the UI. I coded up a few uses and I quickly discovered that my app wanted two different behaviors while making web service calls in the background. These behaviors are:
  1. The background task should operate transparently. It would only alert the user if the operation timed out,
  2. The user had to wait for the operation to complete, but user is given the option to cancel the background operation.
I decided to create a utility class to encapsulate these behaviors and make it all a little easier. Thus I created the BackgroundTask class, which I present here. What does it do? The BackgroundTask class is like a NSThread class; it allows you to spin off code in a separate thread. But this class is more powerful because it supports the two behaviors described above.

The Behaviors

First, let's discuss the desired behaviors in a little more detail. Behavior #1 listed above is for tasks that are low priority and don't require the user to wait for it to complete. The task is kicked off in the background, and the main thread retains control for the user to do whatever they need. They don't even know there is something going on in the background (except for the spinning network access icon in the status bar). The only feedback the user may get is if the operation timed out, meaning it failed. Each task created is given a timeout value. If the operation is not complete by this timeout period, then an alert message pops up that displays something like this:
The alert message displayed below "Operation failed" is also configurable when you create the task object. Behavior #2 is more of a blocking operation, but with the option to cancel it if the user feels it is taking too long. This is useful for higher-priority tasks that require the user to wait until it is complete. For example, if the user hit a refresh button to update the items displayed in a table, it kind of makes sense for the refresh to complete before continuing. When running a task like this, it displays a modal dialog like this:
I added a little optimization to this behavior as well. This dialog won't pop up for X number of seconds, where X is configurable. This is to avoid those situations where the background task completes almost immediately. When this happens, it was silly for the alert to pop up then disappear so fast that the user couldn't even read what it said. So the alert will only come up if the task takes longer than X seconds.

Instantiating

Since the BackgroundTask class supports two different behaviors, it also supports two different initializers. This first one initializes the object for behavior #1, where the task runs the background without any user interaction:
BackgroundTask* task = [[BackgroundTask alloc] initWithTarget:self
   selector:@selector(executeEmailPage:)
   argument:myparent.page
   timeout:40.0
   alertMsg:@"Attempt to email page timed out"]; 
The target/selector arguments specify the method to run in the new thread. The argument is optional data you can pass to the above method. Timeout specifies how long to give that thread before putting up an alert. And the final argument is the alert message to put up when the operation times out. This next initializer is for behavior #2, where the user can cancel the operation:
BackgroundTask* task = [[BackgroundTask alloc]
   initWithTarget:self
   selector:@selector(backgroundRefresh)
   argument:nil
   waitBeforeAlert:0.5
   title:@"Refreshing data"
   msg:@"Standby while refreshing pages"]; 

The target, selector, and argument parameters are the same as the first initializer. The waitBeforeAlert parameter specifies how long to give the operation to complete before putting up the modal box. The title and msg parameters allow you to configure the text displayed in the modal dialog.

Usage

To use the BackgroundTask object after you create it is very simple:
// start the background thread
[task start];

// release it since we don't need it anymore
[task release]; 

Getting The Code

The code for the BackgroundTask class is available on the Osmorphis google group. You can download the .zip archive here. Hopefully its usage is clear enough that you can easily stick the code in your own project and begin using it right away.

2 comments:

  1. the link to the zip file is broken - any other link that I can use?
    Great sharing!

    ReplyDelete
  2. The link in the article works for me. Also try http://sites.google.com/site/osmorphis/BackgroundTask.zip?attredirects=0&d=1

    ReplyDelete