【Kotlin/Android Studio】AlarmManagerの使い方!BroadcastReceiverで時間指定処理

【Kotlin/Android Studio】AlarmManagerの使い方!BroadcastReceiverで時間指定処理

この記事からわかること

  • Android Studio/KotlinAlarmManager実装方法
  • BroadcastReceiverとは?
  • 任意指定した時間処理実行するには?
  • 一定間隔でアラームを繰り返す
  • setメソッドsetExactsetInexactRepeating違い役割

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

参考文献:公式リファレンス:AlarmManager
参考文献:公式リファレンス:PendingIntent

環境

AlarmManagerとは?

AndroidのAlarmManagerとはアラーム機能のように指定した未来の時間に任意の処理を実行させるようにスケジュールを設定できるクラスです。単なる遅延処理ではなくデバイスがスリープ状態であっても処理を実行することができますが、その分バッテリーは大きく消費するので使い分けには注意が必要です。

仕組みとしてはAlarmManagerが特定の時間に、指定したIntentをアクティブにすることで、そのIntentをトリガーとしてサービスの起動やブロードキャスト(システムや他のアプリにイベントを通知する仕組み)の送信が実行されます。

おすすめ記事:【Kotlin/Android Studio】Intentとは?Activity間のデータの受け渡し

Intentをアクティブにする時間の指定はRTC(リアルタイムクロック)や絶対時間で指定することができます。

RTC(リアルタイムクロック)

RTCはいわゆるアラーム機能のような定刻に繰り返しイベントを発行したい際に利用されます。発行したい時間だけを指定し、システムの時刻と一致するたびに発行されます。

絶対時間

特定の日付と時刻に一度だけイベントを発行したい際に利用されます。

時間に正確ではないイベント

Androidでは機能の正確性よりバッテリー消耗を抑えることを一番に考えているのか、普通に使用してもあまり正確な時間にアラームが発行されません。ミリ秒単位で指定できる割には発火に数秒〜数分のずれが生じることが多いです。

AlarmManagerでもアラームの設定メソッドが複数用意されており、より時間に厳格に発火させたい場合は専用のメソッドを使用するようになっています。

AlarmManagerでBroadcastReceiverを使用する

まずは実際にAlarmManager自体の使い方を見ていきます。実装するのは「ボタンタップしてから3秒後にトーストを表示する」機能です。

流れ

  1. BroadcastReceiverクラスの作成
  2. AndroidManifestに設定を追加
  3. 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:不正確な繰り返しを設定するの名前通り、トリガーになる時間とインターバルになる時間に正確性はありません。公式サイトにも「必ずしも毎正時に発生するとは限りません」と記述されていました。

公式リファレンス: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

公式リファレンス:setメソッド

setExact

公式リファレンス:setExactメソッド

setRepeating

公式リファレンス:setRepeatingメソッド

setInexactRepeating

公式リファレンス:setInexactRepeatingメソッド

setAndAllowWhileIdle

公式リファレンス:setAndAllowWhileIdleメソッド

setAlarmClock

公式リファレンス:setAlarmClockメソッド

AlarmManagerの定数の種類と違い

setメソッドなどの第一引数に渡すAlarmManagerの定数の種類と違いをまとめておきます。

AlarmManager.RTC

公式リファレンス:AlarmManager.RTC

システムクロックに基づいてアラームを設定。デバイスをウェイクアップせず、デバイスがスリープ状態のときにオフになると、次にデバイスがウェイクアップ(起動)するまで配信されない。

AlarmManager.RTC_WAKEUP

公式リファレンス:AlarmManager.RTC_WAKEUP

システムクロックに基づいてアラームを設定。デバイスがスリープ状態のときでもデバイスをウェイクアップ(起動)する

AlarmManager.ELAPSED_REALTIME_WAKEUP

公式リファレンス:AlarmManager.ELAPSED_REALTIME_WAKEUP

端末の起動から経過時間を基準にアラームを設定。SystemClock.elapsedRealtimeを使用して指定する場合に指定する(?)

AlarmManagerを使用して通知を実装する

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index