해당 글은 이 영상을 보고 공부한 걸 기록했다.
단순한 시간과 메세지를 저장하기 위해 만든 데이터 클래스
package com.yesnoheun3.alarmmanagerguide
import java.time.LocalDateTime
data class AlarmItem (
val time: LocalDateTime,
val message: String
)
추후의 ViewModel에서 사용할 수 있도록 만든 인터페이스.
실제로 ViewModel에서는 context가 필요하지만 인터페이스는 아니다 - 로컬에서 유닛 테스트를 수행할 수는 없다.
package com.yesnoheun3.alarmmanagerguide
interface AlarmScheduler {
fun schedule(item: AlarmItem)
fun cancel(item: AlarmItem)
}
intent로 보낸 메세지를 받는다.
여기서 리포지토리를 사용하거나, 알림을 받거나 비동기 시스템등을 사용할 수 있다.
AndroirManifest에 등록해야한다!
package com.yesnoheun3.alarmmanagerguide
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// 키값으로 메세지를 받음
val message = intent?.getStringExtra("EXTRA_MESSAGE") ?: return
println("Alarm triggered: $message")
}
}
AlarmManager API를 직접 사용
setExactAndAllowWhileIdle의 파라미터

package com.yesnoheun3.alarmmanagerguide
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import java.time.ZoneId
// Context를 참조함
// AlarmScheduler를 상속
class AndroidAlarmScheduler (private val context: Context): AlarmScheduler {
// AlarmManager 참조 - getSystemService는 OS의 기능을 사용
private val alarmManager = context.getSystemService(AlarmManager::class.java)
@SuppressLint("ScheduleExactAlarm")
override fun schedule(item: AlarmItem) {
// 두번째 파라미터: intent를 보내는 클래스
val intent = Intent(context, AlarmReceiver::class.java).apply {
putExtra("EXTRA_MESSAGE", item.message)
}
// 당양한 set 함수가 있다. set, setExact, setInexactRepeating, ...
// setExactAndAllowWhileIdle: 정확한 시간 + idle(저전력)모드에서도 실행됨
alarmManager.setExactAndAllowWhileIdle(
// 해당 모드: 유저의 디스플레이를 실행한다.
AlarmManager.RTC_WAKEUP,
// toEpochSecond를 사용하려면 minSdk가 26이어야한다.
item.time.atZone(ZoneId.systemDefault()).toEpochSecond() * 1000,
PendingIntent.getBroadcast(
context,
item.hashCode(), // 같은 리퀘스트 코드로 실행될 시 > 업데이트가 된다.
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE // Permission이 필요!
)
)
}
override fun cancel(item: AlarmItem) {
alarmManager.cancel(
PendingIntent.getBroadcast(
context,
item.hashCode(),
Intent(context, AlarmReceiver::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
}
}
package com.yesnoheun3.alarmmanagerguide
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.yesnoheun3.alarmmanagerguide.ui.theme.AlarmManagerGuideTheme
import java.time.LocalDateTime
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val scheduler = AndroidAlarmScheduler(this)
var alarmItem: AlarmItem? = null
setContent {
AlarmManagerGuideTheme {
// A surface container using the 'background' color from the theme
var secondsText by remember {
mutableStateOf("")
}
var message by remember {
mutableStateOf("")
}
Column (modifier = Modifier
.fillMaxSize()
.padding(16.dp), verticalArrangement = Arrangement.Center) {
OutlinedTextField(value = secondsText, onValueChange = {secondsText = it}, modifier = Modifier.fillMaxWidth(), placeholder = {
Text(text = "Trigger alarm in seconds")
})
OutlinedTextField(value = message, onValueChange = {message = it}, modifier = Modifier.fillMaxWidth(), placeholder = {
Text(text = "Message")
})
Row (modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Button(onClick = {
alarmItem = AlarmItem(
time = LocalDateTime.now().plusSeconds(secondsText.toLong()),
message = message
)
alarmItem?.let(scheduler::schedule)
secondsText = ""
message = ""
}) {
Text(text = "Schedule")
}
Button(onClick = {
alarmItem?.let(scheduler::cancel)
}) {
Text(text = "Cancel")
}
}
}
}
}
}
}