Friday, October 8, 2010

Part II - Design patterns for scheduling a batch job in an Android Application

In my previous post we discussed usage of AlarmManager to schedule batch Jobs in Android Applications. In this post, we will dig dipper into design patterns specific to scheduled jobs executing resource intensive operations.

Any process having a Broadcast receiver object, is treated as foreground process and get's priority by Android Runtime. Consider a situation where you have a resource intensive process scheduled as a batch job. Let's say, the user has been playing a rich UI game (with significant frame changes/sec) when AlarmManager triggers your resource intensive job via the Broadcast Notification. Since BroadcastReceiver object gets priority in the order of execution, the user may experience slowness / lag in the application (i.e. the game in this case) running on the UI.

In order to circumvent this problem, it's recommended to design your batch job as a service within your application. Use AlarmManager to trigger the notification and let the BroadcastReciever start (or Bind to) the service to execute your job in the background. In this way, the user would not experience any impact on the application running on the UI. As with other components, don't forget to register the Service in the Manifest.

Sample code for registering a service in the AndroidManifest.xml

 
  
 


Refining further, one last scenario to be considered. What happens if your phone is already in a sleep-mode when the scheduled job gets triggered. Incidentally, you can set the 'type' parameter in the AlarmManager to wake-up the phone while Broadcast is being triggered. The Phone will automatically remain awake as long as the BroadcastReceiver is executing. However once the BroadcastReceiver object starts your service and completes it's scope, we run into the risk of the phone again going to sleep mode, there by stopping your (long running) background job abruptly.

To avoid such unpleasant situation, I recommend to obtain a Wake-Lock from PowerManager system service. Release the Wake-Lock after your background process completes successfully. This way you would ensure that your background process gets all due attention from the Android device.

Sample code for a service implementing a Wake-Lock
public class ScheduledPostService extends Service {

 @Override
 public IBinder onBind(Intent arg0) {
  return null;
 }
 
 @Override
 public void onCreate() {
  // initialize the scheduleTimer
  Log.d(Const.TAG, "Scheduled Post Service created");
 }
 
 @Override
 public void onStart(Intent intent, int startId) {
  
  // get a PowerLock
  // Retain the wake lock till this process completes
    
  PowerManager pm = (PowerManager) getSystemService(getApplicationContext().POWER_SERVICE);
  PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "SMSPost Tag");
  
  // Acquire lock
  wl.acquire();
  
  // perform your operation
  
  // Release lock
  wl.release();
 
 }
 
 @Override
 public void onDestroy() {
  super.onDestroy();
  Log.d(Const.TAG, "Scheduled Post Service destroyed"); 
 }
}

Don't forget to update the Android Manifest, giving permission to the application for using the WakeLock


Hope this has been helpful. Please feel free to share your experiences.

No comments: