한때 이슈가 된 사항이죠. 지금도 뾰족한 해법이 나오지 않은거 같은데 혹시나 해서 질문 올려 봅니다.
매일 자정에 백그라운드에서 작업을 처리해야 하는 앱을 개발중입니다.
안드로이드8 이후로는 일반 앱에서 ACTION_DATE_CHANGED를 수신 못하기 때문에 Job이든 WorkManager든 AlarmManager를 사용해서 매일 자정에 실행되도록 해야 하는데, 문제가 얘네들은 앱이 죽을때 같이 죽어버린다는거죠. 어떤 앱이든 절대, 무조건 오류가 생기지 않도록 만드는건 사실상 불가능한 일이고, 이 때문에 죽지 않는 service, alarmManager 등 여러 대안이 나왔지만 이런것들도 지난 android update에서 전부 막혀버린걸로 보이네요.
그럼 현 상황에서 매일 자정에 무조건 실행이 보장되는 background service를 만드는건 불가능한 건가요?
참고를 위해 덧붙이자면 현재 자정 이벤트 실행 프로세스는 이렇습니다.
1. 앱 설치 시, broadcastReceiver에서 ACTION_PACKAGE_ADDED 이벤트를 수신하고 workManager 실행
2. workManager에서 매일 자정에 trigger되는 alarmManager 등록
관련 소스
class ReceiverForBootOrDateTimeChanged : BroadcastReceiver() {
@SuppressLint("ApplySharedPref")
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
Timber.tag("sdfgdswert").d("$action onReceive()")
if (Intent.ACTION_PACKAGE_ADDED == action || // 패키지 설치
Intent.ACTION_PACKAGE_REPLACED == action || // 교체? 앱 업데이트의 경우인듯?
Intent.ACTION_BOOT_COMPLETED == action || // 부팅
Intent.ACTION_DATE_CHANGED == action || // 날짜 변경(작동 안함?)
Intent.ACTION_TIME_CHANGED == action || // 사용자가 직접 시간 변경
Intent.ACTION_TIMEZONE_CHANGED == action) // 시간대 변경
{ // 위의 이벤트 시 자정에 실행되는 alarm manager 등록 작업 시작
val worker = OneTimeWorkRequestBuilder<WorkerForBootOrDateTimeChanged>()
val workManager = WorkManager.getInstance(context)
workManager.beginUniqueWork(WORK_NAME_FOR_BOOT_OR_DATETIME_CHANGED, ExistingWorkPolicy.REPLACE, worker.build()).enqueue()
if(BuildConfig.DEBUG){
CoroutineScope(Dispatchers.Default).launch {
RoomDatabaseDebug.getInstance(context).debugLogDao().insert("ReceiverForBootOrDateTimeChanged onReceive() action: $action")
}
}
}
}
}
class WorkerForBootOrDateTimeChanged(private val context: Context, params: WorkerParameters): Worker(context, params) {
override fun doWork(): Result {
setAlarmForNextMidnight(context)
return Result.success()
}
}
fun setAlarmForNextMidnight(context: Context) {
Timber.d(" setAlarmForNextMidnight()")
val calendarTomorrow = Calendar.getInstance()
calendarTomorrow[Calendar.HOUR_OF_DAY] = 0
calendarTomorrow[Calendar.MINUTE] = 0
calendarTomorrow[Calendar.SECOND] = 0
calendarTomorrow[Calendar.MILLISECOND] = 0
calendarTomorrow.add(Calendar.DATE, 1)
val intentMidnight = Intent(context, BroadcastReceiverForMidnight::class.java)
val flags = PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
val pendingIntent = PendingIntent.getBroadcast(context, 875976, intentMidnight, flags)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val alarmClockInfo = AlarmManager.AlarmClockInfo(calendarTomorrow.timeInMillis, pendingIntent)
alarmManager.setAlarmClock(alarmClockInfo, pendingIntent)
}