ひさびさのpostでAlarmManager

久々のPostです。そして、今回は軽めな内容でAlarmManagerを使った実装をしたいと思います。

AlarmManagerとは

Systemが提供するAlarm Serviceを使う事が出来る。Alarm Serviceを使う事によって、deviceがsleep状態にあったとしても将来のある時点でapplicationを起動し、処理を実行することが出来ます。(原文、ここ)

実装の要点は下記です

  1. AlarmManagerを取得
  2. Intentを作成
  3. 上記IntentからPendingIntentを作成
  4. AlarmManagerにPendingIntentを登録
  5. 該当のIntentに反応するようにReceiverにIntentFilterを登録
  6. BroadcastReceiverを拡張したclassでAlarmManagerから発行されたIntentを受信した際の処理を記載
  7. 必要に応じてAlarmManagerに登録していたIntentをキャンセル(キャンセルしないとActivityが終了してもAlarmManagerは該当のIntentのAlarmを発行し続ける次回Activity起動時は既にPendingIntentが登録されて状態で起動する)

気づいた点をまとめます

  • AlarmManager#setRepeating()を使用する場合はflagはPendingIntent.FLAG_ONE_SHOTでは駄目(一回通知されて終了してしまう)
  • ELAPSED_REALTIME_WAKEUPは実機がSleep状態でもwakeしてIntentをBroadcastする
  • IntentはIntent#filterEquals()で比較されAction, data, type, class, categoryが同じであれば同じIntentと見なされる。extraは比較対象外

んで実装です

AndroidManifest.xml

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

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".AlarmManagerActivity"
                  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.android.example.AlarmReceiver"/>
    </application>
</manifest>

Activity, BroadcastReceiver

package com.android.example;

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.content.IntentFilter;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class AlarmManagerActivity extends Activity {
    
	private static final String TAG = "AlarmManagerActivity";
	
	private static final String ACTION_TEXT_UPDATE = "com.android.example.ACTION_TEXT_UPDATE";
	
	private Button mAlarmStartButton;
	private Button mAlarmStopButton;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.main);
        
        mAlarmStartButton = (Button)findViewById(R.id.alarm_start_button);
        mAlarmStopButton = (Button)findViewById(R.id.alarm_stop_button);
        
        mAlarmStartButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Context context = AlarmManagerActivity.this;
				//AlarmManagerを取得
				AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
				
				//PendingIntentを作成
				Intent intent = new Intent();
				intent.setAction(ACTION_TEXT_UPDATE);
				//AlarmManager#setRepeating()を使用する場合はflagはPendingIntent.FLAG_ONE_SHOTでは駄目(一回通知されて終了してしまう)
				PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
				
				//AlarmManagerにPendingIntentを登録
				//ELAPSED_REALTIME_WAKEUPは実機がSleep状態でもwakeしてIntentをBroadcastする
				am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 100, 5000, pendingIntent);
			}
		});
        
        mAlarmStopButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Context context = AlarmManagerActivity.this;
				AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
		    	Intent intent = new Intent();
				intent.setAction(ACTION_TEXT_UPDATE);
				PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
				//必要に応じてAlarmManagerに登録していたIntentをキャンセル
                                //キャンセルしないとActivityが終了してもAlarmManagerは該当のIntentのAlarmを発行し続ける
				//次回Activity起動時は既にPendingIntentが登録されて状態で起動する
				//IntentはIntent#filterEquals()で比較されAction, data, type, class, categoryが同じであれば同じIntentと見なされる。extraは比較対象外
				am.cancel(pendingIntent);
			}
		});
        
        //該当のIntentに反応するようにReceiverにIntentFilterを登録
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_TEXT_UPDATE);
        registerReceiver(new AlarmReceiver(), filter);
    }
    
	public class AlarmReceiver extends BroadcastReceiver{
    	
    	//BroadcastReceiverを拡張したclassでAlarmManagerから発行されたIntentを受信した際の処理を記載
		@Override
		public void onReceive(Context context, Intent intent) {
			if(intent.getAction().equals(ACTION_TEXT_UPDATE)){
				Toast.makeText(context, "onReceive", Toast.LENGTH_SHORT).show();
			}
		}
    }
}

って、こんな感じです。次回もManager系のpostしたいと思います。

HashTag #java, #android, #PendingIntent, #BroadcastReceiver, #AlarmManager, #IntentFilter