http://www.developer.com/ws/android/programming/Handling-Lengthy-Operations-with-Android-App-Widgets-3843561.htm

Back to article

Handling Lengthy Operations with Android App Widgets


October 30, 2009

Introduction

Android home screens enjoy the benefits of the App Widget framework, from displaying pictures to indicating the weather. In our previous articles on the App Widgets (listed in the reference section), we illustrated how to create a simple App Widget which allows for user interaction. Specifically, we built an App Widget that displayed a slideshow of images, where the slideshow controls were made available to the user. In these examples, the slideshow images were sourced locally from the device, but this is not terribly realistic for a real App Widget. Many developers will wish to have their App Widgets display information that is retrieved remotely, for example, from an RSS. Therefore, we now turn our attention to handling some "background processing" in conjunction with an App Widget. In this article, you will learn how to add background downloading of the images by using a Service object. To do this, we will build upon our previous App Widget example.

The following tasks need to be performed to add background processing to our existing App Widget:

  1. Create a new Service
  2. Modify the App Widget to call in to the Service, as needed
  3. Add code to the Service to handle downloading of images
  4. Add threading to the Service for each on-screen widget

That's it in a nutshell. Each App Widget update (as handled in the onUpdate() method of the AppWidgetProvider) sends a message to the Service, starting it, as if it wasn't already started. This message must contain all the information needed to control a specific instance of the App Widget. The Service may already know about the App Widget, in which case it just updates it. Otherwise, the Service must launch a new thread to handle background downloading of the images for this particular App Widget instance. The instance is identified by the App Widget's appWidgetId value.

The Configuration screen has also been updated (see Figure 1). It now includes an entry for a URL to a compatible image feed. For this example, we use an Atom XML feed with image enclosures.


Figure 1: Updated Configuration screen with image feed setting.

Working with Android Services

Android services are loosely defined as background processes or executables that can be accessed from other applications. They can be started, to run in the background, or they can be directly connected to by means of a remote interface. However, an App Widget can't directly connect to a Service, no Broadcast receiver can. If you attempt to connect to a Service with an App Widget, an exception is thrown:

 

  android.content.ReceiverCallNotAllowedException: 
     IntentReceiver components are not allowed to bind to services

 

To keep things simple, the primary interface to our Service will be via a mechanism we're already using: the SharedPreferences object. A Service can be started any number of times. However, only a single stop request is needed to shut it down. When starting the Service, we'll use Intent data to communicate what appWidgetId (App Widget instance) is starting up. This way, we can track all displayed widgets using just a single Service.

Starting the Service--or sending an update to it--is as easy as creating and posting an Intent. The following code demonstrates how to do this, including passing some control information to the Service:

 

  Intent intent = new Intent(context, WidgetService.class);
  
  intent.putExtra(WidgetService.EXTRA_FLAG_REQUEST_STOP, requestStop);
  intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
  intent.putExtra(WidgetService.EXTRA_FLAG_UPDATE_IMAGE, updateImage);
  
  context.startService(intent);

 

Although the method call to send this Intent is called startService(), it only starts the Service if it's not already running. There is no internal reference counting. A single call to stop the Service will stop it, regardless of how many times the startService() method is called. Since we may have multiple App Widgets using the Service, we've added a requestStop flag to specifically stop the thread for a particular instance of our App Widget.

On the Service-side of the startService() call is our onStart() handler. In short, the handler checks to see if a thread is already running for the specific App Widget instance. If not, one is started. If one is already running, the handler either stops it (if requestStop is true) or updates the image (if updateImage is true). Additionally, it updates the image in case the UI state has changed. The App Widget image updates proceed as they did in the previous examples, with the exception that the images are being pushed out from the Service. Here's some pseudo-code (edited for clarity; see code download for details) for this process:

 

  public void onStart(Intent intent, int startId) {
  
     super.onStart(intent, startId);
     int appWidgetId = intent.getIntExtra(
        AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
  
     boolean updateImage = intent.getBooleanExtra(
        WidgetService.EXTRA_FLAG_UPDATE_IMAGE, false);
     boolean stopRequested = intent.getBooleanExtra(
        WidgetService.EXTRA_FLAG_REQUEST_STOP, false);
  
     if (threadPool.containsKey(appWidgetId)) {
        UpdateThread thread = threadPool.get(appWidgetId);
  
        imageUrl = getNextImageUrl();
        updateWidget(this, appWidgetId, imageUrl);
  
     } else {
        UpdateThread thread = new UpdateThread(appWidgetId);
        threadPool.put(appWidgetId, thread);
        thread.start();
        /* wait for first image to download */
        imageUrl = getNextImgeUrl();
        updateWidget(this, appWidgetId, imageUrl);
     }
  }

 

Although the details have been removed, the flow is relatively straightforward. The threadPool is simply a Hashtable to keep track of the thread for each App Widget instance. If the thread exists, we use it. If not, we start one and add it to the threadPool.

Downloading the Images within the Android Service

The UpdateThread, shown briefly in the previous section, handles the downloading of the images from the feed. All this thread does is wait around for a specific amount of time to pass before downloading an XML file, parsing it for images, then downloading those images--also on a separate worker thread. The details around the XML parsing and image downloading are up to the developer; any method will work fine. The result, however, is important. The images must be stored locally and a reference should be kept in a Vector attached to the UpdateThread. Vector objects are synchronized for us, so we can use this to pass the image references easily back to the main Service. This reference is then used during an App Widget update to configure the RemoteViews object.

Sidebar: Cautions

Although this article has illustrated how to allow for App Widgets requiring lengthy background operations using an Android Service, there are numerous housekeeping items to be aware of for production code that have not been addressed in our example. For example, when writing networking code, care should be taken to avoid excessive network data calls which could result in surprising bills to end-users. Busy background services should also do their best to avoid draining the battery of the handset. Additionally, the App Widget service should manage its resources prudently (e.g. by limiting its local storage requirements). For example, each image file could be resized before it's stored to reduce the disk space required by the App Widget. Along the same lines, the image files themselves should be persistently indexed to survive across handset resets. Many methods are available for handling these issues; however, the details of how are up to the developer and the specific App Widget implementation.

Summary

In this article, you've learned how to create a helper Service to handle background processing for an App Widget. In addition, this same Service is used to download the images the App Widgets will display in its slideshow.

References

Creating a Home Screen App Widget on Android
Handling User Interaction with Android App Widgets

About the Authors

Shane Conder is a software developer focused on mobile and web technologies. He is currently working at a small mobile software company. With almost two decades of experience in software production, Lauren Darcey specializes in the development of commercial grade mobile applications. Recently, Shane and Lauren coauthored an in-depth programming book entitled Android Wireless Application Development, available from Addison-Wesley (ISBN: 0321627091). They are now working on an entry-level Android book, coming in Spring 2010. They can be reached at androidwirelessdev+a5@gmail.com and via their blog at http://androidbook .blogspot.com.

Sitemap | Contact Us