【Kotlin/Android Studio】How to use AlarmManager! Time-specified processing with BroadcastReceiver

【Kotlin/Android Studio】How to use AlarmManager! Time-specified processing with BroadcastReceiver

What you will learn from this article

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

index

[open]

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

みんなの誕生日

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

posted withアプリーチ

Official Reference: AlarmManager
Official Reference: PendingIntent

Environment

What is AlarmManager

Android's AlarmManager is, like an alarm function, a class that allows you to schedule arbitrary processing to be executed at a specified future time. It's not just a delayed process; it can execute processes even when the device is in a sleep state, but this consumes a significant amount of battery, so care must be taken in its use.

The mechanism involves AlarmManager activating a specified Intent at a specific time, and this Intent then triggers the launch of a service or the sending of a broadcast (a mechanism to notify the system or other apps of an event).

Recommended Article: [Kotlin/Android Studio] What is an Intent? Passing data between Activities

The time for activating an Intent can be specified using RTC (Real-Time Clock) or absolute time.

RTC (Real-Time Clock)

RTC is used when you want to repeatedly issue events at a fixed time, like a typical alarm function. You specify only the time you want it to fire, and it fires each time it matches the system time.

Absolute Time

This is used when you want to issue an event only once at a specific date and time.

Events That Are Not Time-Accurate

Perhaps because Android prioritizes reducing battery consumption over functional accuracy, even with normal use, alarms are often not triggered at the exact specified time. Despite being able to specify time in milliseconds, delays of several seconds to several minutes in firing are common.

AlarmManager provides multiple methods for setting alarms, and if you want an alarm to fire more strictly on time, you should use a dedicated method.

Using BroadcastReceiver with AlarmManager

First, let's look at how to use AlarmManager itself. We will implement a feature to "display a toast 3 seconds after a button tap".

Flow

  1. Create BroadcastReceiver class
  2. Add settings to AndroidManifest
  3. Set alarm (broadcast) from MainActivity

1. Creating the BroadcastReceiver Class

First, we define a class that inherits from BroadcastReceiver, which is executed when a broadcast is received. The onReceive method is the method that is actually executed when a broadcast is received.


class ReceivedActivity : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // Processing to execute when a broadcast is received
        Toast.makeText(context, "Broadcast received!", Toast.LENGTH_SHORT).show()
    }
}

2. Adding Configuration to AndroidManifest

Next, declare the class that will receive broadcasts in the manifest file. Be aware that without this, you cannot receive broadcasts.


<application
    <!-- Omitted -->
    <activity
      <!-- Omitted -->
    </activity>

    <!-- Add -->
    <receiver android:name=".ReceivedActivity"
        android:process=":remote" />
</application>

3. Setting the Alarm (Broadcast) from MainActivity

From here, we will use the AlarmManager class to set an alarm for a future time. The detailed processing flow is left in the comments.


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 {
        // Get AlarmManager instance
        val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        // Generate an Intent
        val notificationIntent = Intent(this, ReceivedActivity::class.java)
        // If you want to pass values from the Activity, store them here
        notificationIntent.putExtra("Message", "Message you want to pass")
        // Get PendingIntent for broadcasting
        // The second argument, requestCode, can be 0 if there's only one broadcast to register, but needs to be changed if there are multiple -> e.g., data ID
        val pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)

        // Define the execution time
        // SystemClock.elapsedRealtime : Method to get elapsed time in milliseconds since system boot
        val triggerTime = SystemClock.elapsedRealtime() + 3000L // 3 seconds later
        // Set the alarm
        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, pendingIntent)
    }
  }
}

With this, an alarm event will be issued once, 3 seconds after tapping the button. The set method used at the end is the part that actually sets the alarm. In this implementation, the broadcast was issued approximately 3 seconds later without any particular deviation in the firing time.

Specifying with Absolute Time

If you want to set an alarm by explicitly specifying the date and time and want the broadcast to be issued at an exact time, use the setExact method instead of the set method. This method sets the event to be scheduled as accurately as possible to the specified time, but it is officially recommended not to use unnecessarily exact alarms. Also, when using methods that guarantee accuracy, you need to add the following two permissions to the manifest file.


<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>

To specify the time, create a Calendar instance, set the desired date and time, get the millisecond representation using the timeInMillis method, and pass it to the alarmManager.setExact method.


val timeZone = TimeZone.getTimeZone("Asia/Tokyo")
val calendar = Calendar.getInstance(timeZone)   // Specify the time zone
calendar.timeInMillis = System.currentTimeMillis() // Initialize with current time (or 0 to reset completely)
calendar.set(Calendar.YEAR, 2023)               // Set any year
calendar.set(Calendar.MONTH, Calendar.OCTOBER)  // Set any month
calendar.set(Calendar.DAY_OF_MONTH, 22)         // Set any day
calendar.set(Calendar.HOUR_OF_DAY, 11)          // Set any hour
calendar.set(Calendar.MINUTE, 32)               // Set any minute
calendar.set(Calendar.SECOND, 0)                // Set any second
val triggerTime = calendar.timeInMillis         // Get the millisecond representation of the specified date and time

alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent)

Repeating Alarms at Regular Intervals

If you want to repeat alarm events at regular intervals, implement it as follows using setRepeating or setInexactRepeating.

val triggerTime = SystemClock.elapsedRealtime() + 3000L // 3 seconds later
val intervalTime = 10000L // 10-second interval

alarmManager.setInexactRepeating(
    AlarmManager.ELAPSED_REALTIME_WAKEUP,
    triggerTime,
    intervalTime,
    pendingIntent
)

As the name setInexactRepeating (Set an inexact repeating alarm) suggests, the trigger time and interval are not exact. The official documentation also states, "it will not necessarily occur exactly on the hour".

Official Reference: setInexactRepeating

Incidentally, the interval duration can also be specified using AlarmManager constants as follows.

alarmManager.setInexactRepeating(
    AlarmManager.ELAPSED_REALTIME_WAKEUP,
    triggerTime,
    AlarmManager.INTERVAL_DAY, // 1 day later
    pendingIntent
)

Registering Multiple Broadcasts

If you want to register multiple broadcasts, use the setType method to specify a type name for the Intent so it can be identified as a different type, and pass a non-duplicate number to the second argument requestCode of the getBroadcast method. For example, using a data ID is convenient.


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 {
        // Assume 'id' is a unique integer for each alarm
        val id = System.currentTimeMillis().toInt() // Example of a unique ID
        val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val notificationIntent = Intent(this, ReceivedActivity::class.java)
        // Specify the Intent type
        notificationIntent.setType("NotifyIntent:" + id.toString())
        notificationIntent.putExtra("Message", "Message you want to pass for ID: " + id)
        // The second argument, requestCode, can be 0 if there's only one broadcast to register, but needs to be changed if there are multiple -> e.g., data ID
        val pendingIntent = PendingIntent.getBroadcast(this, id, notificationIntent, PendingIntent.FLAG_IMMUTABLE)

        // SystemClock.elapsedRealtime : Method to get elapsed time in milliseconds since system boot
        val triggerTime = SystemClock.elapsedRealtime() + 10000L // 10 seconds later
        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, pendingIntent)
    }
  }
}

In implementing alarm functions or notifications, it's often the case that you want to use the same Intent (ReceivedActivity in this instance) to execute it multiple times at different times, so this can be achieved using the code above.

Cancelling an Alarm

If you want to cancel a registered broadcast, etc., simply specify the Intent and execute the cancel method on both the PendingIntent and the AlarmManager.

// Assume 'context' and 'id' (the one used for registration) are available
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 or PendingIntent.FLAG_NO_CREATE) // Use FLAG_NO_CREATE if you only want to cancel an existing one

if (pendingIntent != null) {
    pendingIntent.cancel()
    alarmManager.cancel(pendingIntent)
}

Types and Differences of AlarmManager Alarm Settings

Several methods for setting alarms have been introduced, so their roles and features are summarized here.

set

Official Reference: set method

setExact

Official Reference: setExact method

setRepeating

Official Reference: setRepeating method

setInexactRepeating

Official Reference: setInexactRepeating method

setAndAllowWhileIdle

Official Reference: setAndAllowWhileIdle method

setAlarmClock

Official Reference: setAlarmClock method

Types and Differences of AlarmManager Constants

The types and differences of AlarmManager constants passed as the first argument to methods like set are summarized here.

AlarmManager.RTC

Official Reference: AlarmManager.RTC

Sets an alarm based on the system clock (wall clock time). Does not wake up the device; if the device is asleep when it goes off, it will not be delivered until the device next wakes up.

AlarmManager.RTC_WAKEUP

Official Reference: AlarmManager.RTC_WAKEUP

Sets an alarm based on the system clock (wall clock time). Wakes up the device even if it is asleep.

AlarmManager.ELAPSED_REALTIME

Official Reference: AlarmManager.ELAPSED_REALTIME

Sets an alarm based on the elapsed time since the device booted (using SystemClock.elapsedRealtime()). Does not wake the device. If the device is asleep, the alarm will not be delivered until it next wakes up.

AlarmManager.ELAPSED_REALTIME_WAKEUP

Official Reference: AlarmManager.ELAPSED_REALTIME_WAKEUP

Sets an alarm based on the elapsed time since the device booted. Wakes up the device if it is asleep. This is specified when using SystemClock.elapsedRealtime() to designate the trigger time.

Implementing Notifications Using AlarmManager

I am still learning, so if you find any mistakes or shortcomings, I would appreciate it if you could let me know.

Thank you for reading.

Search Box

Sponsor

ProFile

ame

Hobbies: reading, learning programming, website creation, blogging

I studied to get an IT pass in order to overcome my dislike of IT, and website creation became my hobby lol. Now I'm running this site completely by myself without using a CMS~

New Article

index