Android – How to make time scheduled service

I have been trying to implement a service that will be scheduled on certain periods of time, for example when users selects an action in application, service should be scheduled  to run every five minutes for one hour processing some data (read some data from specified url and store that information into local database). After one hour has passed service should stop and can be scheduled again by the user. . To implement something like this there are several challenges and it took me some time to figure out how I should implement this following Android best practices. In this article I am going to explain how I believe something like this should be implemented and you are all welcomed to leave your comments regarding the solution.

Let me start first with one small diagram that explains how this solution should work.

Untitled Diagram (3)

 

If you look at the diagram above you shall see that whole process starts with users initiating it trough some action. This action should be specifying url address and hitting button to start. This button action will trigger url to be written down for example in database  together with value of current time plus one hour and than at last schedule alarm manager to notify Alarm Broadcast receiver in 5 minutes starting from now. Also one more note there is another broadcast receiver (Service Broadcast Receiver) registered with application dedicated to receive notifications from the service in case application is in focus so that it can fetch data from database processed and written int database by  the service.

Coming back to the moment when alarm manager triggers notification, Alarm Broadcast Receiver gets notification and starts service so that it can do it’s job. Looking at the service and its execution, once started by receiver, service first takes url and end time from database, fetches data from url and writes it into database. Next step it checks if end time has passed, in case it has not passed creates new schedule for alarm manager so that it can be woken up in 5 minutes starting from now, in case end time has passed there is no schedule to be created against alarm manager and service will not run again until users starts process again. In both cases last step in one run of a service is to send notification to Service Broadcast Receiver so that application can be updated.

One more thing to add here regarding the service is that we need to make sure mentioned service can be interrupted by CPU going to sleep. This can mean that data can be left inconsistent and we can not guarantee that service will be executed every 5 minutes. How to take care of this situation will be explained in the code of  Alarm Broadcast Receiver and Service itself.

Now that we have this process defined, let’s start with looking at code how this can be implemented. First look at the layout of the application interface (app_layout.xml), simple and clean, it has one input text box for url and a button.


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
   <TextView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:textAppearance="?android:attr/textAppearanceMedium"
      android:text="Enter url to read data"
      android:id="@+id/head_label"
  />
  <EditText
     android:id="@+id/url"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginTop="16dp"
     android:layout_marginLeft="4dp"
     android:layout_marginRight="4dp"
     android:layout_marginBottom="4dp"
     android:hint="URL" />

   <Button
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="Start"
      android:id="@+id/startButton"
      android:layout_marginLeft="5px"/>

</LinearLayout>

And main activity should handle that url is written in database together with end time and to schedule alarm manager to notify Alarm Broadcast Receiver:


public class MainActivity extends Activity{
   //database helper class, we want go into implementation
   //details of this class since it is out of the scope of this article
   private DbHelper dbHelper = DbHelper.getInstance();
   //receiver that will receive events from service
   private BroadcastReceiver serviceBroadcastReceiver;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.app_layout);
      Button b = (Button)findViewById(R.id.startButton);
      b.setOnClickListener(new View.OnClickListener(){
         @Override
         public void onClick(View view){
            //take url value, calculate end date and write into database
            String url = ((EditText)findViewById(R.id.url)).getText().toString();
            Date endTime = new Date(System.currentTimeMillis() + 3600000)
            dbHelper.writeUrlAndEndTime(url, endTime);
            //register Alarm Broadcast Reveiver
            Intent intent = new Intent(getApplicationContext(), AlarmBroadcastReceiver.class);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 0);
            //schedule alarm manager in five minutes to send notification to receiver
            AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
            alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+300000 , pendingIntent);
         }
      }
      //this broadcast receiver here receives a message as extra from intent set by Service
      serviceBroadcastReceiver = new BroadcastReceiver(){
          @Override
          public void onReceive(Context context, Intent intent) {
             Toast.makeText(context, "Service processed data!, Toast.LENGTH_LONG);
          }
      }
   }
   //register receiver on application start and unregister receiver when application stops
   @Override
   protected void onStart() {
      super.onStart();
      LocalBroadcastManager.getInstance(this).registerReceiver(serviceBroadcastReceiver, new IntentFilter("SERVICE"));
   }

   @Override
   protected void onStop() {
      LocalBroadcastManager.getInstance(this).unregisterReceiver(serviceBroadcastReceiver);
      super.onStop();
   }
}

Next step is to implement Alarm Broadcast Receiver:


public class AlarmBroadcastReceiver extends WakefulBroadcastReceiver {

   @Override
   public void onReceive(Context context, Intent intent) {
      Intent service = new Intent(context, Service.class);
      startWakefulService(context, service);
   }
}

As you can see class AlarmBroadcastReceiver extends WakefulBroadcastReceiver class, this class ensures that receiver receives event from Alarm Manager and starts Service . Also holds wake lock when service starts and passes intent with method startWakefulService that has extra that represents wake lock. This way Service is able to release wake lock once it does it’s job. For details take a look at WakefulBroadcastReceiver.


public class Service extends IntentService{

   private LocalBroadcastManager broadcastManager;
      protected void sendTimeNotification(){
      Intent intent = new Intent(AppConstants.TIMER_MSG);
      broadcastManager.sendBroadcast(intent);
   }

   @Override
   protected void onHandleIntent(Intent intent) {
      //get url and end time from database
      dbHandler dbHandler = dbHandler.getInstance(this);
      String url = dbHandler.getUrl();
      Date endTime = dbHandler.getEndTime();
      //read some data from url
      String data = processUlr(url);
      dbHandler.writeData(data);
      if(endTime.getTime()-System.currentTimeMillis()>0){
         //schedule wake in 5 minutes from now
         Intent intent = new Intent(getApplicationContext(), AlarmBroadCastReceiver.class);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 0);
         AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
         alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 300000 , pendingIntent);
      }
      Intent intentApp = new Intent("SERVICE");
      broadcastManager.sendBroadcast(intentApp);
      //release wake lock
      AlarmBroadCastReceive.completeWakefulIntent(intent);
 }
}

Finally lets take a look at the AndoidManifest.xml


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >

<application
android:allowBackup="true"
android:icon="@drawable/test_app"
android:label="@string/app_name"
>
<activity
   android:name=".MainActivity"
   android:label="@string/app_name" >
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>
<receiver android:name="com.anjadev.receiver.AlarmBroadcastReceiver" />
<service android:name="com.anjadev.service.Service" android:enabled="true"/>
</application>
   <!--This permission is important so that we can use wake lock for service -->
   <uses-permission android:name="android.permission.WAKE_LOCK"/>
</manifest>

With this manifest solution is complete and also this small tutorial, please leave your comments in case you notice any problem in the solution. There might be some minor issues in the code since it is taken from project of mine and some parts I have removed in order to simplify the code for this tutorial.

Have a nice day!

Posted in Android Development Tagged with: , , , , , ,
2 comments on “Android – How to make time scheduled service
  1. Raghu says:

    Nice Android Articles. Thanks for sharing your research which are helpful to others.

    It would be excellent if you can share the code somewhere like Github.

    Cheers 🙂
    Raghu

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Subscribe for Newsletter

Categories