【Kotlin/Android Studio】AlarmManagerの使い方!BroadcastReceiverで時間指定処理
この記事からわかること
- Android Studio/KotlinでAlarmManagerの実装方法
- BroadcastReceiverとは?
- 任意の指定した時間に処理を実行するには?
- 一定間隔でアラームを繰り返す
- setメソッドやsetExact、setInexactRepeatingの違いと役割
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
参考文献:公式リファレンス:AlarmManager
参考文献:公式リファレンス:PendingIntent
環境
- Android Studio:Flamingo
- Kotlin:1.8.20
AlarmManagerとは?
AndroidのAlarmManagerとはアラーム機能のように指定した未来の時間に任意の処理を実行させるようにスケジュールを設定できるクラスです。単なる遅延処理ではなくデバイスがスリープ状態であっても処理を実行することができますが、その分バッテリーは大きく消費するので使い分けには注意が必要です。
仕組みとしてはAlarmManagerが特定の時間に、指定したIntentをアクティブにすることで、そのIntentをトリガーとしてサービスの起動やブロードキャスト(システムや他のアプリにイベントを通知する仕組み)の送信が実行されます。
おすすめ記事:【Kotlin/Android Studio】Intentとは?Activity間のデータの受け渡し
Intentをアクティブにする時間の指定はRTC(リアルタイムクロック)や絶対時間で指定することができます。
RTC(リアルタイムクロック)
RTCはいわゆるアラーム機能のような定刻に繰り返しイベントを発行したい際に利用されます。発行したい時間だけを指定し、システムの時刻と一致するたびに発行されます。
絶対時間
特定の日付と時刻に一度だけイベントを発行したい際に利用されます。
時間に正確ではないイベント
Androidでは機能の正確性よりバッテリー消耗を抑えることを一番に考えているのか、普通に使用してもあまり正確な時間にアラームが発行されません。ミリ秒単位で指定できる割には発火に数秒〜数分のずれが生じることが多いです。
AlarmManagerでもアラームの設定メソッドが複数用意されており、より時間に厳格に発火させたい場合は専用のメソッドを使用するようになっています。
AlarmManagerでBroadcastReceiverを使用する
まずは実際にAlarmManager自体の使い方を見ていきます。実装するのは「ボタンタップしてから3秒後にトーストを表示する」機能です。
流れ
- BroadcastReceiverクラスの作成
- AndroidManifestに設定を追加
- MainActivityからアラーム(ブロードキャスト)を設定
1.BroadcastReceiverクラスの作成
最初にブロードキャストを受け取った際に実行されるBroadcastReceiver
を継承したクラスを定義していきます。onReceive
メソッドが実際にブロードキャストを受け取った際に実行されるメソッドです。
class ReceivedActivity : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// ブロードキャストを受け取った際に実行したい処理
Toast.makeText(context, "ブロードキャストを受け取ったよ", Toast.LENGTH_SHORT).show()
}
}
2.AndroidManifestに設定を追加
続いてマニフェストファイルにブロードキャストを受け取るクラスを宣言しておきます。これがないとブロードキャストを受け取ることはできないので注意してください。
<application
<!-- 省略 -->
<activity
<!-- 省略 -->
</activity>
<!-- 追加 -->
<receiver android:name=".ReceivedActivity"
android:process=":remote" />
</application>
3.MainActivityからアラーム(ブロードキャスト)を設定
ここからAlarmManager
クラスを使用して未来の時刻にアラームをセットしていきます。細かい処理の流れはコメントに残しておきました。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val alarmButton: Button = findViewById(R.id.button)
alarmButton.setOnClickListener {
// AlarmManagerインスタンスを取得
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
// インテントを生成
val notificationIntent = Intent(this, ReceivedActivity::class.java)
// Activityから値を渡したい場合は格納しておく
notificationIntent.putExtra("メッセージ", "渡したいメッセージ")
// ブロードキャストを行うためのPendingIntentを取得
// 第二引数requestCodeは登録するブロードキャストが1つなら0で良いが複数あるなら変更する必要有→例えばデータのIDなど
val pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
// 実行される時間を定義
// SystemClock.elapsedRealtime : システム起動後の経過時間をミリ秒単位で取得するメソッド
val triggerTime = SystemClock.elapsedRealtime() + 3000L // 3秒後
// アラームの設定
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, pendingIntent)
}
}
}
これでボタンをタップしてから3秒後に一回だけアラームイベントが発行されます。最後に使用したset
メソッドが実際にアラームを設定している部分です。この実装の場合は特に発行時間がズレることなく約3秒後にブロードキャストが発行されました。
絶対時間で指定する
日付と時刻を明示的に指定してアラームを設定したい場合は正確な時間にブロードキャストを発行したいのでset
メソッドではなくsetExact
メソッドを使用します。このメソッドは出来るだけ指定された時間と正確な時間にイベントがスケジュールされるように設定してくれますが、公式からは不必要に正確なアラームを使用しないことが推奨されています。また正確さを保証するメソッドを使用する場合はマニフェストファイルに以下の2つを追加する必要があります。
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
時間の指定方法はCalendar
インスタンスを生成し、任意の日時と時刻をセットしてtimeInMillis
メソッドでミリ秒表現を取得を取得してalarmManager.setExact
メソッドに渡せばOKです。
val timeZone = TimeZone.getTimeZone("Asia/Tokyo")
val calendar = Calendar.getInstance(timeZone) // タイムゾーンを指定
calendar.timeInMillis = 0 // リセット
calendar.set(Calendar.YEAR, 2023) // 任意の年を設定
calendar.set(Calendar.MONTH, Calendar.OCTOBER) // 任意の月を設定
calendar.set(Calendar.DAY_OF_MONTH, 22) // 任意の日を設定
calendar.set(Calendar.HOUR_OF_DAY, 11) // 任意の時を設定
calendar.set(Calendar.MINUTE, 32) // 任意の分を設定
calendar.set(Calendar.SECOND, 0) // 任意の秒を設定
val triggerTime = calendar.timeInMillis // 指定した日時のミリ秒表現を取得
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent)
一定間隔でアラームを繰り返す
アラームを一定間隔でアラームイベントを繰り返したい場合はsetRepeating
またはsetInexactRepeating
を使用して以下のように実装します。
val triggerTime = SystemClock.elapsedRealtime() + 3000L // 3秒後
val intervalTime = 10000L // 10秒のインターバル
alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
triggerTime,
intervalTime,
pendingIntent
)
setInexactRepeating:不正確な繰り返しを設定する
の名前通り、トリガーになる時間とインターバルになる時間に正確性はありません。公式サイトにも「必ずしも毎正時に発生するとは限りません」と記述されていました。
ちなみにインターバルの間隔はAlarmManager
の定数を使用して以下のように指定することも可能です。
alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
triggerTime,
AlarmManager.INTERVAL_DAY, // 1日後
pendingIntent
)
複数のブロードキャストを登録する
複数のブロードキャストを登録しておきたい場合はsetType
メソッドを使用してインテントに別のタイプと識別できるようにタイプ名を指定し、getBroadcast
メソッドの第二引数requestCode
に重複しない番号を渡します。例えばデータのIDなどを利用すると便利です。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val alarmButton: Button = findViewById(R.id.button)
alarmButton.setOnClickListener {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val notificationIntent = Intent(this, ReceivedActivity::class.java)
// インテントのタイプを指定しておく
notificationIntent.setType("NotifyIntent:" + id.toString())
notificationIntent.putExtra("メッセージ", "渡したいメッセージ")
// 第二引数requestCodeは登録するブロードキャストが1つなら0で良いが複数あるなら変更する必要有→例えばデータのIDなど
val pendingIntent = PendingIntent.getBroadcast(this, id, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
// SystemClock.elapsedRealtime : システム起動後の経過時間をミリ秒単位で取得するメソッド
val triggerTime = SystemClock.elapsedRealtime() + 10000L // 10秒後
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, pendingIntent)
}
}
}
アラーム機能や通知の実装では同じIntent(今回はReceivedActivity
)を使用して異なる時間に複数回実行させたい場合の方が多いと思うので上記のコードを使用すると実現可能です。
アラームのキャンセル
登録してあるブロードキャストなどをキャンセルしたい場合はIntentを指定してcancel
メソッドを実行するだけです。
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val notificationIntent = Intent(context, ReceivedActivity::class.java)
notificationIntent.setType("NotifyIntent:" + id.toString())
val pendingIntent = PendingIntent.getBroadcast(context, id, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
pendingIntent.cancel();
alarmManager.cancel(pendingIntent)
AlarmManagerのアラーム設定の種類と違い
アラームを設定するためのメソッドがいくつか登場したのでそれぞれの役割や特徴をまとめておきます。
set
- アラームを指定された時間にスケジュールするメソッド
- 指定された時間が過去の場合はすぐに発行
- トリガー時間は不正確(指定時間より前に配信されないが、延期される可能性有)
setExact
- アラームを指定された時間に正確にスケジュールするメソッド
- 指定された時間が過去の場合はすぐに発行
- 要求されたトリガー時間にできるだけ近い時刻に配信
- 多様はNG
setRepeating
- アラームを指定された時間と指定された間隔にスケジュールするメソッド
- 指定された時間が過去の場合はすぐに発行
- トリガー時間/インターバル時間は比較的正確(繰り返しは基本不正確)
setInexactRepeating
公式リファレンス:setInexactRepeatingメソッド
- アラームを指定された時間と指定された間隔にスケジュールするメソッド
- 指定された時間が過去の場合はすぐに発行
- トリガー時間/インターバル時間は不正確
setAndAllowWhileIdle
公式リファレンス:setAndAllowWhileIdleメソッド
- アラームを指定された時間にスケジュールするメソッド
- 指定された時間が過去の場合はすぐに発行
- システムが低電力アイドル(Doze/ドーズ)モードにある場合でも実行
setAlarmClock
- アラームを指定された時間にスケジュールするメソッド
- 指定された時間が過去の場合はすぐに発行
- 目覚まし時計を表すアラームをスケジュール
- デバイスを起動して、画面ON、音を鳴らす、振動してユーザーに通知することを期待
- システムが低電力アイドル(Doze/ドーズ)モードにある場合でも実行
AlarmManagerの定数の種類と違い
set
メソッドなどの第一引数に渡すAlarmManagerの定数の種類と違いをまとめておきます。
AlarmManager.RTC
システムクロックに基づいてアラームを設定。デバイスをウェイクアップせず、デバイスがスリープ状態のときにオフになると、次にデバイスがウェイクアップ(起動)するまで配信されない。
AlarmManager.RTC_WAKEUP
公式リファレンス:AlarmManager.RTC_WAKEUP
システムクロックに基づいてアラームを設定。デバイスがスリープ状態のときでもデバイスをウェイクアップ(起動)する
AlarmManager.ELAPSED_REALTIME_WAKEUP
公式リファレンス:AlarmManager.ELAPSED_REALTIME_WAKEUP
端末の起動から経過時間を基準にアラームを設定。SystemClock.elapsedRealtime
を使用して指定する場合に指定する(?)
AlarmManagerを使用して通知を実装する
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。