Sunday, 15 March 2015

Alarm Manager in Android: How to use it

AlarmManager provides a mechanism to schedule your application to run at a pre specified time. This class provide to access the device alarm service. When alarm kick off, it run the Intent registered with it through your application. This is very useful mechanism for Asynchronous execution of some operation inside the application whether your application is currently active or not or the device is in the sleep mode.

How to use it ?
To use this api inside the application, you have to do at least the following three steps.

Get handle of AlarmManager

Get the instance of AlarmManager via getSystemService method of Context class. Pass the name of the service as Context.ALARM_SERVICE.

public class MainActivity extends Activity {

AlarmManager alarmManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

}
}

Create Intent and PendingIntent

Create an Intent you want to execute at future time. This Intent will be used inside the PendingIntent object. PendingIntent will be used to set the AlarmManager object which is created in the above code snippet.

Intent intent = new Intent(getApplicationContext(), PictureBoarcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);

In the above code snippet, a BroadcastReceiver is registered with the intent object. Here PendingIntent is created using static method getBroadcast method of PendingIntent class, because we are registering a broadcast receiver not an activity. If we have to register another activity, then we have use one of the overloaded method of getActivity methods defined inside the PendingIntent class.

Also observe the last parameter of the getBroadcast method, it is used as PendingIntent.FLAG_UPDATE_CURRENT. This parameter indicates that whenever you try to perform this PendingIntent multiple times, each time it will update the current intent only. It will replace the extra data only which are associated with the Intent inside the PendingIntent. Other values for this parameter are FLAG_CANCEL_CURRENT, FLAG_NO_CREATE, FLAG_ONE_SHOT.

Set the PendingIntent to AlarmManager

alarmManager.set (AlarmManager.RTC_WAKEUP,
                              System.currentTimeMillis() + 5000,
                              pendingIntent);

In the above snippet, set method of AlarmManager requires three parameters. First parameter is used to wake up the device if it is in sleep. Second parameter is the time at which you want to fire your intent. Third one is the PendingIntent which carries your Intent inside it.

An example application using AlarmManager

In this simple example, I have created a custom BroadcastReceiver inside the MainActivity class. This broadcastreceiver update the ImageView after a specified time period. This time is set via the EditText box. The main activity layout also contains two buttons. One is used to fire the intent which occurs after the time period and second button to reset the ImageView. A toast is displayed when you click the “Change Picture” button that shows that you have to wait for the time period before the Broadcast receiver update the image. Some of the screenshots are given below.

am.png

Remember to add the your custom BoradcastReceiver in the manifest file, Otherwise Intent will not be fired upon clicking the button.

MainActivity.java

package test.example.alarmmanagertest;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

EditText timeText;
Button change, reset;
static ImageView image;

AlarmManager alarmManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

timeText = (EditText) findViewById(R.id.editText1);
change = (Button) findViewById(R.id.button1);
image = (ImageView) findViewById(R.id.imageView1);
reset = (Button)findViewById(R.id.button2);
reset.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
image.setImageResource(R.drawable.ic_launcher);
}
});

change.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {
String waitTime = timeText.getText().toString();
int time = Integer.parseInt(waitTime);
Toast.makeText(getApplicationContext(), "Wait for " + time+"s",
Toast.LENGTH_SHORT) .show();
Intent intent = new Intent(getApplicationContext(),
PictureBoarcastReceiver.class);
intent.putExtra("TIME", time);

PendingIntent pendingIntent = PendingIntent.getBroadcast(
getApplicationContext(), 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + time*1000, pendingIntent);
}
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

public static class PictureBoarcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
int time = intent.getIntExtra("TIME", -1);
Toast.makeText(context, "After " + time+"s", Toast.LENGTH_SHORT)
.show();
image.setImageResource(R.drawable.mic_red);
}

}
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center_horizontal"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="test.example.alarmmanagertest.MainActivity" >

   <TextView
       android:id="@+id/textView1"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="Alarm Manager Test"
       android:textSize="20sp" />

   <TextView
       android:id="@+id/textView2"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_below="@+id/textView1"
       android:layout_marginTop="15dp"
       android:text="Set Time" />

   <EditText
       android:id="@+id/editText1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignBaseline="@+id/textView2"
       android:layout_alignBottom="@+id/textView2"
       android:layout_alignLeft="@+id/imageView1"
       android:inputType="number"
       android:ems="10" >

       <requestFocus />
   </EditText>

   <ImageView
       android:id="@+id/imageView1"
       android:layout_width="150dp"
       android:layout_height="150dp"
       android:layout_below="@+id/editText1"
       android:layout_centerHorizontal="true"
       android:layout_marginTop="60dp"
       android:src="@drawable/ic_launcher" />

   <Button
       android:id="@+id/button1"
       style="?android:attr/buttonStyleSmall"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentBottom="true"
       android:layout_centerHorizontal="true"
       android:layout_marginBottom="59dp"
       android:text="Change Picture" />

   <Button
       android:id="@+id/button2"
       style="?android:attr/buttonStyleSmall"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentBottom="true"
       android:layout_centerHorizontal="true"
       android:layout_marginBottom="16dp"
       android:text="Reset Image" />

</RelativeLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="test.example.alarmmanagertest"
   android:versionCode="1"
   android:versionName="1.0" >

   <uses-sdk
       android:minSdkVersion="15"
       android:targetSdkVersion="19" />

   <application
       android:allowBackup="true"
       android:icon="@drawable/ic_launcher"
       android:label="@string/app_name"
       android:theme="@style/AppTheme" >
       <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=".MainActivity$PictureBoarcastReceiver"
           android:enabled="true">
       </receiver>
   </application>

</manifest>

I hope this may help you to implement your own Alarm based services…;)