AlarmManager을 통해 서비스를 작동시켜보자!
우선 알람을 Scheduler를 통해 보낸다.
class AndroidAlarmScheduler (private val context: Context): AlarmScheduler {
private val alarmManager = context.getSystemService(AlarmManager::class.java)
@SuppressLint("ScheduleExactAlarm") // 1
override fun schedule(item: AlarmItem) {
val intent = Intent(context, AlarmReceiver::class.java).apply {
putExtra("EXTRA_MESSAGE", item.message)
}
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
item.time.toEpochSecond() * 1000,
PendingIntent.getBroadcast(
context,
item.hashCode(),
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE // 2, 3
)
)
}
Lint는 생길 수 있는 버그나 최적화를 체크하는 툴이다. SuppresLint는 해당 주석이 달린 컴포넌트에 대한 경고를 무시한다. minSdkVersion 이후의 API를 사용할 때 올라오는 경고를 무시하기 위해 사용한다.PendingIntent.FLAG_UPDATE_CURRENT: 보류 중인 Intent가 존재할 경우 extra 데이터는 새 intent의 extra 데이터로 업데이트 해야한다.PendingIntent.FLAG_IMMUTABLE: 생성된 PendingIntent는 수정할 수 없다. 추후에 send 메서드에 추가된 Intent 파라미터는 무시된다. (Android 12부터 PendingIntent는 반드시 해당 플래그를 사용해야 한다.)이제 이걸 BroadcastReceiver가 받는다.
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val serviceIntent = Intent(context, AlarmService::class.java) // 1
if (intent != null){
serviceIntent.putExtras(intent)
}
val versionNum = Build.VERSION.SDK_INT
if(versionNum >= Build.VERSION_CODES.S){ // 2
val pendingIntent = PendingIntent.getForegroundService(context, 1, serviceIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
pendingIntent.send()
} else if (versionNum >= Build.VERSION_CODES.O){ // 3
context?.startForegroundService(serviceIntent)
} else { // 4
context?.startService(serviceIntent)
}
}
}
PendingIntent를 통해 서비스를 시작한다.Android 12부터는 알림 트램폴린으로 사용되는 서비스 또는 브로드캐스트 리시버에서 액티비티를 시작할 수 없다. 즉 이 안에서 startActivity를 호출할 수 없다. 대신 PendingIntent를 사용해야 한다.
출처: https://developer.android.com/reference/android/content/Context#startForegroundService(android.content.Intent)
context.startForegroundService를 통해 시작한다. 이 API는 26부터 추가됐다.26 이상부터 해당하는 이야기: 오래 사용하는 백그라운드의 경우 포그라운드 서비스 - 알림을 표시해야 한다고 한다. 포그라운드 서비스 자체가 상태바 알림을 보여주는 용도이기에 notification을 써야 한다고. 여기서는 포그라운드 서비스 중
Media를 사용한다.
startService를 통해 시작한다. 26 이전에는 포그라운드 서비스 권한 없이 자유롭게 백그라운드 서비스를 시작할 수 있다고 한다.API level 30 ~ 33은 카메라, 마이크를 사용할 경우 manifest에 작성해야하고,
API level 34 이상은 모든 포그라운드 서비스를 maniftest에 등록해야 한다.
이제 이를 통해 Service를 시작한다.
class AlarmService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null){
println("No Alarm Triggered: no Intent")
stopSelf()
return START_NOT_STICKY
}
val notification = createNotification()
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){ // 1
startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
} else { // 2
startForeground(1, notification)
}
} catch (e: Exception) {
e.printStackTrace()
stopSelf()
return START_NOT_STICKY
}
val message = intent.getStringExtra("EXTRA_MESSAGE")
println("Alarm triggered: ${message}")
return START_STICKY
}
...