目覚まし時計アプリだったり、定期的なプッシュ通知を出すなどに便利なAlarm。
しかし、複数のアラームをセットした時がなかなか曲者です。
不要なアラームはまとめて削除したい。その方法を考えます。
結論としては、requestCodeを変えて登録しておくと良い、と言った感じです(詳しくは後半)。
前提
複数登録するアラームは、同じBroadcastReceiverを呼びます。
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
// AlarmReceiver: アラーム起動で呼ばれるクラス、BroadcastReceiverをextendsしてある
基本的な登録と削除の方法 (1つの場合)
基本的には、登録したIntentと同じIntentを使用してキャンセルします。
コードを見るのが早いと思うので、実装を以下に。
// アラームの登録
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 0);
alarmManager.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, pendingIntent);
// calendar: Calendarのインスタンス、アラームが起動する時刻を設定してある
// AlarmReceiver: アラーム起動で呼ばれるクラス、BroadcastReceiverをextendsしてある
// アラームの削除
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 0);
pendingIntent.cancel();
alarmManager.cancel(pendingIntent);
見ての通り、登録・削除で違いがあるのは、AlarmManagerにsetしているかcancelしているか程度です。
alarmManagerだけキャンセルしてもできるようですが、pendingIntentもcancelしておくほうが良さそうです。。。
複数のAlarmを登録・削除する場合
今回の場合は、特に**「Receiverが同じ」**場合です。ここでは、複数のパターンを考えます
Intent.setTypeを使うパターン
intent.setType()を使用して、別のintentであることを明確にするパターンです。
まずは、実装例を見てみます。
登録処理
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent1 = new Intent(getApplicationContext(), AlarmReceiver.class);
intent1.setType("first alarm"); // このsetTypeが重要
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(getApplicationContext(), 0, intent1, 0);
alarmManager.setRepeating(AlarmManager.RTC, calendar1.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, pendingIntent1);
Intent intent2 = new Intent(getApplicationContext(), AlarmReceiver.class);
intent2.setType("second alarm"); // このsetTypeが重要
PendingIntent pendingIntent2 = PendingIntent.getBroadcast(getApplicationContext(), 0, intent2, 0);
alarmManager.setRepeating(AlarmManager.RTC, calendar2.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, pendingIntent2);
上のコードではsetType
でintentを区別するようにしました。
実際に、以下のコードでintentが区別されていることを確認することができます。
intent1.filterEquals(intent2); // returns false.
setTypeしなかった場合
intent1.filterEquals(intent2); // returns true.
intent1.putExtra(..., ...);
intent1.filterEquals(intent2); // returns true.
intent.putExtraとかしても、trueになる = 同じintentとして扱われるのでご注意。setTypeが重要です。
削除処理
例によって、同じインテントを作って、削除するので、ここでもsetTypeが重要です。
先にセットした2つのアラームのうち、2つめを削除する例を挙げてみます。
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
intent.setType("second alarm"); // このsetTypeが重要
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 0);
pendingIntent.cancel();
alarmManager.cancel(pendingIntent);
これで2つめのアラームだけをキャンセルすることができました!
setTypeの問題点
setTypeで、intentを区別することができる事がわかりました。しかし、問題点もあります。
すべてキャンセルしたい時の処理が地味に面倒
仮に、アプリのアップデート時に過去のアラームをすべて削除する場合、過去に使用したアラームのタイプをすべてチェックして、削除する必要があります。
すべてのユーザが、直前のバージョンからアップデートするわけではありませんので、過去のタイプを、持ち続けなければならないんですね。。。
(むしろアプリを再インストールしてもらいたい)
requestCodeを分けるパターン
上では、setTypeでintentを区別する方法を上げましたが、キャンセル処理が面倒だということがわかりました。もっとすっきり考えられる方法はないのか。
ここではrequestCodeを使った方法を考えます。
登録と削除を一気に処理
基本的には、requestCodeをincrementしていく運用を考えます。
そうすれば、必要なrequestCodeより小さいものは全てキャンセルする処理が可能になります。
int ACTIVE_REQUEST_CODE = 12; // たとえば、requestCodeが12以上のアラームを有効にしておくとして
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// 不要になった過去のアラームをまとめて削除する
// requestCodeを0から登録していたとする
for (int requestCode = 0; requestCode < ACTIVE_REQUEST_CODE; requestCode++) {
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), requestCode, intent, 0);
pendingIntent.cancel();
alarmManager.cancel(pendingIntent);
}
// 新しいアラームの登録、必要なアラームの再登録
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), ACTIVE_REQUEST_CODE, intent, 0);
alarmManager.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, pendingIntent);
この実装にしておけば、アップデートで一気にアラーム周りを刷新する場合においても、簡潔なコードで書けるのではないかなと予想しております。
結論
以上のような検討から、やはり、
アラームを複数登録する場合は、その後の運用も考えて、requestCodeをincrementして登録していくのが良さそう!!