[Kotlin] Android 使用 AlarmManager 設定定時任務

在 Android 中要建立精確時間的任務會使用 AlarmManager ,就算 App 沒有在啟動中也可以自動觸發,但是 AlarmManager 會比較耗電。
本文示範使用 AlarmManager 在指定的時間顯示 Toast 訊息。

要使用 AlarmManager 需要在 AndroidManifest.xml 中註冊權限:
    
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

</manifest>
    

建立一個 DTO 用來儲存通知資料
    
import java.time.LocalDateTime

data class MyAlarmDto(
    val time: LocalDateTime, // 觸發時間
    val message: String,
)
    

AlarmManager 任務依照時間觸發後會發送 Intent 廣播,這裡建立 MyAlarmBroadcastReceiver.kt,用來接收任務觸發後發送的 Intent 事件和訊息,並且將內容使用 Toast 顯示:
    
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast


class MyAlarmBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent) {
    
        val data = intent.getStringExtra("data") ?: ""
        Toast.makeText(context, "訊息: $data", Toast.LENGTH_SHORT).show()
    }
}
    

在 AndroidManifest.xml 中註冊 MyAlarmBroadcastReceiver.kt ,使其能夠接收廣播
    
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

    <application>
        <receiver android:name=".MyAlarmBroadcastReceiver"/>
    </application>
    
</manifest>
    

建立 MyAlarmScheduler.kt ,方便使用 AlarmManager 建立和取消任務:
    
class MyAlarmScheduler(private val context: Context) {

    private val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    fun scheduleAlarm(item: MyAlarmDto) {

        // 建立 Intent 並設定要傳遞的資料
        val intent = Intent(context, MyAlarmBroadcastReceiver::class.java).apply {
            putExtra("data", item.message)
        }

        // 建立 PendingIntent
        val broadcast = PendingIntent.getBroadcast(
            context, item.id, intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        // 設定精確時間任務
        alarmManager.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP,
            item.time.atZone(ZoneId.systemDefault()).toEpochSecond() * 1000, // 設定觸發時間
            broadcast
        )
    }

    fun cancelAlarm(item: MyAlarmDto) {
        alarmManager.cancel(
            PendingIntent.getBroadcast(
                context,
                item.id,
                Intent(context, MyAlarmBroadcastReceiver::class.java),
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )
        )
    }
}
    

建立和取消定時任務

這裡使用 Jetpack Compose 建立兩個最簡單的 Button 做示範,按下後就會建立 5 秒鍾執行的任務,也可以在觸發前取消
    
    val content: Context = LocalContext.current
    val myAlarmManager: MyAlarmScheduler = MyAlarmScheduler(content)
    val myDto: MyAlarmDto = MyAlarmDto(
        id = 1,
        time = LocalDateTime.now().plusSeconds(5),
        message = "測試訊息"
    )


    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.Center
    ) {
        Button(onClick = {
            myAlarmManager.scheduleAlarm(myDto)
        }) {
            Text("建立任務")
        }
        Button(onClick = {
            myAlarmManager.cancelAlarm(myDto)
        }) {
            Text("取消任務")
        }
    }
    



參考資料:
Android Developers - AlarmManager

留言