본문 바로가기
안드로이드 자바

[JAVA][Android] 알람 앱 구현하기 - (3) AlarmManager 살펴보기

by teamnova 2024. 7. 13.
728x90

안녕하세요.

이전 시간에 리사이클러뷰로 사용해서 알람 데이터를 CURD 하는 방법에 대해서 알아보았습니다.

관련된 내용은 아래 링크를 참고해주세요.

2024.07.05 - [안드로이드 자바] - [JAVA][Android] 알람 앱 구현하기 - (1) 리사이클러뷰로 목록 만들기

 

[JAVA][Android] 알람 앱 구현하기 - (1) 리사이클러뷰로 목록 만들기

안녕하세요.안드로이드에서 알람 앱 예제를 구현해보려고 합니다. 이 앱에서는 매일 원하는 시간에 알람이 울리도록 설정하고, 이미 설정된 알람을 수정하거나 삭제할 수 있도록 할 것입니다.

stickode.tistory.com

 

2024.07.08 - [안드로이드 자바] - [JAVA][Android] 알람 앱 구현하기 - (2) SharedPreferences로 알람 데이터 CRUD

 

[JAVA][Android] 알람 앱 구현하기 - (2) SharedPreferences로 알람 데이터 CRUD

안녕하세요.오늘은 알람 앱 구현하기 두번째 시간입니다. 첫번째 시간에는 알람 목록을 띄우기 위한 리사이클러뷰를 만들었는데요.이번에는 그 코드를 바탕으로 SharedPreferences를 사용해서 알람

stickode.tistory.com

 

오늘은 실제로 알람을 설정하기에 앞서서 해당 기능을 구현하는데 사용할 AlarmManager에 대해서 살펴보겠습니다.

 

1. AlarmManager 란?

 

안드로이드 공식문서에 따르면, AlarmManager를 사용해서 알람을 설정하면 앱의 생명주기와 상관없이 시간을 기반으로 작업을 예약할 수 있게 된다고 합니다. 예를 들어, 하루에 한번 정해진 시간에 서비스를 실행시켜서, 최신 데이터를 다운로드 받는 작업을 예약할 수 있습니다.

 

AlarmManager를 통해 설정한 알람의 특징은 다음과 같습니다.

  • 지정된 시간에 또는 정해진 간격으로 인텐트를 실행: 알람이 울릴 때 지정된 인텐트를 실행합니다.
  • Broadcast Receiver와 함께 사용: JobScheduler나 WorkManager를 통해 다른 작업을 실행할 수 있습니다.
  • 앱 외부에서 작동: 앱이 실행되고 있지 않을 때나 기기가 절전 상태인 경우에도 이벤트나 작업을 트리거할 수 있습니다.
  • 리소스 효율성: Timer나 지속적으로 실행 중인 서비스에 의존하지 않기 때문에, 앱의 리소스 요구 사항을 최소화할 수 있습니다.

이런 특징을 가지는 이유는 AlarmManager가 시스템 알람 서비스에 액세스해서 알람을 설정하는 방식으로 동작하기 때문입니다. 즉, AlarmManager를 통해 설정한 알람은 앱 내부에서 동작하는 것이 아닌 시스템 측에서 동작합니다.

지정한 시간에 알람이 울리면, 등록된 인텐트가 시스템에 의해 브로드캐스트되어 대상 앱을 실행시키며, 이때 앱이 실행 중이지 않으면 자동으로 시작되게 합니다.

하지만 기기를 재부팅하게 되면 시스템에 등록한 알람도 지워지기 때문에, 재부팅 시에도 알람이 유지되도록 하기 위해서는 별도의 설정이 필요합니다.

 

2. AlarmManager의 주요 메서드

setExact(int type, long triggerAtMillis, PendingIntent operation)

: 지정된 시간에 한 번 실행되는 정확한 알람을 설정합니다.

 

setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)

: 지정된 시간부터 시작하여 특정 간격으로 반복 실행되는 알람을 설정합니다.


setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)

: 지정된 시간부터 시작하여 특정 간격으로 반복 실행되지만 정확하지 않은 알람을 설정합니다.


setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation)

: Doze 모드에서도 정확한 알람을 설정합니다.

 

3. 부정확한 알람 vs 정확한 알람

정확한 시간에 알람이 울리도록 하기위해서는 시스템 리소스를 많이 요구합니다. 따라서 공식문서에서는 정확한 알람을 사용해야 하는 몇가지 사례를 제외하고 대부분의 경우 부정확한 알람을 사용하도록 권장하고 있습니다.

 

(출처 : 공식문서 https://developer.android.com/develop/background-work/services/alarms/schedule?hl=ko)

 

그러나 우리는 알람시계 앱을 만들려고 하고 있고, 알람시계는 사용자가 설정한 바로 그 시간에 알람이 울리는 것이 매우 중요하기 때문에, 정확한 알람을 사용해야 합니다.

 

4. 정확한 알람 설정을 위한 권한 선언 및 요청 

앱이 Android 12 이상을 타겟팅한다면 '알람 및 리마인더' 특수 앱 액세스 권한을 얻어야 합니다. 이렇게 하려면 다음 코드 스니펫과 같이 앱의 매니페스트 파일에서 SCHEDULE_EXACT_ALARM 권한을 선언합니다.

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

 

앱이 Android 13 (API 수준 33) 이상을 타겟팅한다면 SCHEDULE_EXACT_ALARM 또는 USE_EXACT_ALARM 권한을 선언할 수 있습니다.

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

USE_EXACT_ALARM

  • 자동으로 부여됨
  • 사용자가 취소할 수 없음
  • 예정된 Google Play 정책이 적용됩니다.
  • 제한된 사용 사례

SCHEDULE_EXACT_ALARM

  • 사용자가 부여함
  • 더 광범위한 사용 사례
  • 앱에서 권한이 취소되지 않았는지 확인해야 합니다.

SCHEDULE_EXACT_ALARM 권한과 USE_EXACT_ALARM 권한은 동일한 기능을 나타내지만 사용방법이 다릅니다. USE_EXACT_ALARM와 달리 SCHEDULE_EXACT_ALARM 권한은 사용자가 부여해야 합니다. 사용자와 시스템 모두 SCHEDULE_EXACT_ALARM 권한을 취소할 수 있습니다. 

 

앱에 권한이 부여되었는지 확인하려면 정확한 알람을 설정하기 전에 canScheduleExactAlarms()를 호출합니다. 앱의 SCHEDULE_EXACT_ALARM 권한이 취소되면 앱이 중지되고 향후 정확한 알람이 모두 취소됩니다. 즉, canScheduleExactAlarms()에서 반환된 값이 앱의 전체 수명 주기 동안 유효합니다.

 

앱에 SCHEDULE_EXACT_ALARMS 권한이 부여되면 시스템은 앱에 ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED 브로드캐스트를 전송합니다. 앱은 다음을 실행하는 BroadcastReceiver를 구현해야 합니다.

 

사용자에게 SCHEDULE_EXACT_ALARM 권한을 부여하도록 요청하는 예시 코드는 다음과 같습니다.

import android.content.Context;
import android.content.Intent;
import android.provider.Settings;

public class ExactAlarmPermissionRequester {

    public static void requestExactAlarmPermission(Context context) {
        Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
        context.startActivity(intent);
    }
}

 

5. 반복 알람 설정

알람을 반복적으로 울리도록 설정하려면 다음과 같이 할 수 있습니다.

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;

public class RepeatingAlarmScheduler {

    public static void setRepeatingAlarm(Context context, long triggerAtMillis, long intervalMillis, int requestCode) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        if (alarmManager != null) {
            alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, intervalMillis, pendingIntent);
        }
    }
}

 

 

6. Doze 모드에서도 실행되는 알람 설정

Doze 모드에서도 실행되는 정확한 알람을 설정하는 예제입니다.

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

public class ExactAlarmScheduler {

    public static void setExactAlarm(Context context, long triggerAtMillis, int requestCode) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        if (alarmManager != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
            } else {
                alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
            }
        }
    }
}

 

 

7. 기기 재부팅 후 알람 재설정

기기가 재부팅되면 시스템에 등록된 알람도 지워지기 때문에, 재부팅 시에도 알람을 유지하도록 별도의 설정이 필요합니다. 이를 위해 BroadcastReceiver를 사용하여 BOOT_COMPLETED 이벤트를 처리합니다.

 

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            // 알람을 다시 설정하는 로직을 여기에 추가합니다.
        }
    }
}

 

AndroidManifest.xml에 리시버를 등록합니다.

<receiver android:name=".BootReceiver" android:enabled="true" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

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