Android 공부

Android 백그라운드 정책 톺아보기

1. 서론

꾸준하게 개발자를 괴롭히는 백그라운드 정책

 API 버전이 마시멜로우인 23이상부터 Doze모드AppStand by의 개념이 도입되었습니다. 안드로이드 앱을 개발하는 개발자라면, Doze모드와 AppStand by를 대처해야한다는 말을 들어봤을 것입니다. 그리고 이를 실제로 대응하면서, 껄끄러웠던 경험이 있었을 것이라고 생각합니다. 필자도 '백그라운드에서 시간을 체크하는 작업', '백그라운드에서 네트워크 통신을 하는 작업'등을 경험을 해봤고, 이번에 'FCM에서 네트워크 통신을 사용'하는 경험을 통해서 Doze모드와 각종 백그라운드 정책에 직면을 했습니다. 그래서 이번 포스팅을 통해서 안드로이드의 백그라운드 제한 작업에 함께 알아보려고 합니다.

 

2. Doze 모드 (Android 6.0)

그림 1. 잠자기 모드에서 배터리 수명을 개선하기 위해 첫 번째 레벨의 시스템 액티비티 제한을 적용하는 방법

 Doze 모드는 '기기를 오래동안 사용하지 않은 경우 앱의 백그라운드 CPU 및 네트워크 활동을 지연시켜 배터리를 절약하는 목적을 갖고 있는 기능이 제공됩니다. 그래서 위의 그림과 같이 screen off, stationary on battery가 특정시간 동안 진행되면, Doze 모드로 진입이 되어 네트워크 및 백그라운드 작업에 제한이 생깁니다. 이러한 제한은 maintanance window의 시점에서야 일괄적으로 처리됩니다.

그래서, Doze 모드는 아래와 같은 조건일 때 발생합니다.

  •  기기가 움직이지 않고, 스크린이 특정시간 꺼져 있을 때
  •  기기에 전원공급이 없을 때

 

2-1. Doze 모드에서 발생하는 일들

 Doze 모드에 진입하게 되면, 아래와 같은 일들이 발생하게 됩니다.

  • 네트워크 액세스가 정지됩니다. 
  • 시스템에서 wake lock을 무시합니다. 
  • 표준 AlarmManager 경보(setExact() 및 setWindow() 포함)가 다음 maintenance window로 지연됩니다. 
  • 시스템에서 Wi-Fi 검색을 수행하지 않습니다. 
  • 시스템에서 동기화 어댑터 실행을 허용하지 않습니다. 
  • 시스템에서 JobScheduler 실행을 허용하지 않습니다.

2-2. Doze 모드 대응 방법

3. Doze의 변화 (Android 7.0)

그림 2. 기기가 일정 시간 동안 정지 상태에 있은 후에 잠자기 모드에서 두 번째 레벨의 시스템 액티비티 제한을 적용하는 방법

 Android 7.0 SDK로 변화하면서, Doze 모드 또한 더 체계적으로 변하게 됩니다. 우선, 이전의 Doze 모드에서는 '디바이스가 움직이지 않는다면'이라는 전제조건이 붙으면서, 사용자가 걷거나 하는 경우에는 Doze모드로 진입하지 못 했습니다. 가장 먼저 변한 것은 사용자가 움직이더라도 사용자가 기기의 플러그를 뽑고, 화면이 꺼져 있는 동안 Doze 모드에 진입할 수 있다는 점입니다.

 그리고, 이제 Doze 모드는 2단계에 걸쳐서 실행됩니다. 이것을 Light-Doze 모드, Deep-Doze 모드로 분리해서 부릅니다. Light-Doze 모드는 maintenance window가 잦게 실행되지만, 네트워크의 통신과 싱크, Job 작업들이 지연되게 됩니다. 그리고, Deep-Doze 모드에 진입하게 되면, 이전의 Doze 모드와 같이 알람, 싱크, Job 작업들이 지연되며, GPS, Wi-fi Scans, 네트워크 통신이 제한됩니다. 

 

3-1. 제한 상황

Setting Job Alarm Network FCM
Doze Active 다음 maintaince window 일반 알람: 다음
maintaince window

유휴 상태 중 알람:
최대 9분 연기됨
다음 maintaince window High Priority : 제한 없음
Normal Priority : 다음 maintaince window

 

4. AppStand by의 목적 (Android 6.0)

 AppStand by는 최근 사용자와 상호작용이 없는 앱의 백그라운드 네트워크 활동을 지연시킵니다. 아래의 경우가 모두 포함되지 않는다면, 해당 앱은 AppStand by 상태로 빠지게 됩니다.

  • 사용자가 명시적으로 앱을 실행합니다.
  • 앱 프로세스가 현재 포그라운드에 있습니다(활동 또는 포그라운드 서비스로 있거나 다른 활동 또는 포그라운드 서비스에 사용되고 있음).
  • 사용자가 잠금 화면이나 알림 목록에서 볼 수 있는 알림을 앱에서 생성합니다.
  • 앱이 활성 기기 관리 앱(예: 기기 정책 컨트롤러)입니다. 이러한 앱은 일반적으로 백그라운드에서 실행되지만 기기 관리 앱은 언제든 서버에서 정책을 수신할 수 있게 유지되어야 하므로 앱 대기 모드를 시작하지 않습니다.

 만약, 디바이스가 오랜 시간동안 AppStand by 상태가 된다면, 앱은 하루에 한 번 maintenance window를 획득하게 됩니다. 그리고, 디바이스가 충전되게 된다면, AppStand by 상태에서 해제되어 미뤄진 작업들을 처리하게 됩니다.

5. Background Limits (Oreo)

그림3. 오레오 디바이스에서 백그라운드 작업 제한

 Oreo에 진입하면서, 각종 백그라운드 작업들이 제한이 되었습니다. 우리가 흔히 알고 있는 Service, Broadcast, GPS 작업 등을 다루고 있습니다. 백그라운드 작업들은 기기의 제한된 리소스(RAM)을 사용합니다. 이러한 경우에, 사용자의 리소스를 소모하게 되는데, Oreo에서는 이러한 점을 제한하고, 리소스를 관리하기 위한 제한이 추가됩니다.

 

5-1. Background Service 제한

 우선, Background Service를 제한했습니다. 안드로이드는 앱을 Foreground와 Background로 구분을 합니다. Foreground 상태로 구분짓는 조건은 아래와 같습니다. 

- Activity가 보이는 경우

- Foreground Service가 돌고 있는 경우

- IME, 배경화면 서비스, 알림 리스너, 음성 또는 텍스트 서비스

해당 조건에 한해서는 Foreground 상태로 분리하고, 그 외의 상태는 Background로 구분짓습니다.

처음에 특정 시간동안에는 Service가 사용되겠지만, 해당 Background의 작업은 앱이 일정시간 Background로 진입하게 되면, 앱은 idle 상태가 되면서 Service에 중지 메소드를 실행한 것처럼 중지시킵니다.

 

5-2. Background Service 작업하기

- Background 작업을 임시허용 목록으로 사용하기

 Background Service는 아래와 같은 조건에 임시허용 목록에 허용됩니다. 임시허용 목록이란, 계속해서 Background 작업을 사용할 수 것이 아닌, 일정 시간동안 Background 작업을 할 수 있는 조건입니다.

- Background 작업을 실행하기

 '공부 시간 타이머'와 같이, Background 작업을 꾸준하게 사용해야하는 경우에는 Service를 Foreground로 실행시켜 줍니다.

'담다' 앱에서는 공부 시간 타이머를 위해서 아래와 같이 Service를 관리하고 있습니다. 

* Service 실행하는 로직

Oreo를 분기로 해서, Service를 실행하는 로직을 아래와 같이 startForegroundService로 작업을 해 줍니다

val studyTimerService = Intent(activity, StudyTimerService::class.java)
studyTimerService.putExtra("todayStudyTime", todayWorker)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
	startForegroundService(studyTimerService)
} else {
	startService(studyTimerService)
}

* Service가 작업되는 로직

intent가 null인 경우가 나올 수 있으므로, START_NOT_STICKY를 반환해주고, timer를 실행시켜줍니다.

서비스가 종료된다면, timer를 정지시켜주고, 서비스를 stopForeground 시켜줍니다.

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
	if(intent==null)
	{
		return START_NOT_STICKY;
	}
	playTimer()
	return START_STICKY
}

override fun onDestroy() {
	super.onDestroy()
	stopTimer()
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
		stopForeground(true);
	} else {
		stopSelf();
	}
}

6. App Standby buckets

그림4. 다양한 상태를 갖고 있는 App Standby buckets

 pie 버전에서는 App Standby bukets이 추가되었습니다. 앱의 상태를 다섯가지로 구분지어 최근에 얼마나 자주 사용되었는가를 기준으로 실행시키게 됩니다. Active, WorkingSet, Frequent, Rare, Never의 상태로 나누어서 5가지로 구분을 하고, 해당 상태에 따라서, FCM, Network, Alarm, Job등 각자의 기능들을 지연시키거나 제한합니다. 해당 기준은 안드로이드의 알고리즘을 통해서 머신러닝이 구분짓는다고 합니다.

상태 Job Alarm Network FCM
Active 제한 없음 제한 없음 제한 없음 제한 없음
Working Set 2시간 지연 6분 지연 제한 없음 제한 없음
Frequent 8시간 지연 20분 지연 제한 없음 1일 : Priority high 기준으로 10회
Rare 24시간 지연 2시간 지연 24시간 지연 1일 : Priority high 기준으로 5회
Never 제한 제한 제한 제한

 이와 같이 App Standby Bucket에 의해서 각자 다른 제한사항이 있다는 것을 알 수 있습니다.

7. App Restrictions & Battery Saver

그림5. 배터리 절약 모드

  Job Alarm Network FCM GPS 비고
App Restrictions 제한 제한 제한 제한 제한  
Battery Saver 제한 제한 제한 가능 스크린 off 동안 Foreground에서는 가능

 안드로이드에서는 배터리가 없을 때, 배터리를 아끼기 위해서 자동으로 App Restrictions 모드를 활성화 시킵니다. App Restrictions 모드는 우리가 흔히 알고 있는 '절전 모드'이며, Job, Alarm, Network, FCM, GPS 모두를 제한하게 됩니다. 

 그리고, Battery Saver는 안드로이드 Pie에서 제공하고 있는 기능으로, Forground의 기능은 모두 가능하지만,
Job, Alarm, FCM, Network가 제한되고, 스크린이 꺼져있는 동안 GPS가 제한되게 됩니다.

8. 각자 다른 제한사항에서 대처 방법

 여기까지 글을 읽었다면 도즈모드, 백그라운드 서비스, App Standby Buckets와 같은 다양한 배터리 관리 사항들이 생겼다는 것을 공감할 수 있을 것입니다. 그렇다면, 우리들은 이런 제한을 대응하기 위해서 어떻게 해야할 지, 고민을 해야하겠는데 https://youtu.be/-7eZL3XRqas 해당 영상에서 이에 대한 해답을 명확하게 제시해주고 있습니다. 그래서, 이에 대해서 명확하게 개념을 얻기 위해서는 꼭 2번씩 영상을 보고 이해 하셨으면 좋겠습니다.

 

8-1. 제한 상황

 계속해서, 각종 제한 사항에서는 충전중이지 않은 상태일 때, Network, GPS, FCM, Alarm과 같이 디바이스가 백그라운드에서 작업을 처리하는 행위들을 제한하고 있다는 사실을 이야기하고 있습니다.

 

-Alarm의 실행 로직

알람의 백그라운드 로직

- FCM의 실행 로직

FCM의 백그라운드 로직

- Job의 실행 로직

Job의 백그라운드 로직

8-2. 백그라운드 작업은 WorkManager와 함께

 해당 글에서는 WorkManager까지 다루지는 않을 것이다. 하지만, 각종 백그라운드 제한 사항이 생김에 따라서, 고려해야할 사항이 많은데 그럴 때 손 쉽게 해결할 수 있는 해결책 중 하나는 WorkManager이다. 또한 모든 것의 정답은 WorkManager는 아니다. 아래와 같은 상황 때, WorkManager를 사용하게 되면 손 쉽게 해결 할 수 있다.

WorrrrrkManager

 

9. 톺아보기

 해당 내용들을 학습하고 난 후에는 내가 겪었던 백그라운드 이슈 사항들이 단순히 도즈모드에 한정되지 않고, 다양한 백그라운드 정책들과 함께 엮여있다는 사실을 알게 되었다.

 그래서, 'Android 백그라운드 정책 톺아보기' 라는 글을 작성하게 되었고, 초기의 도즈모드부터 배터리 세이브, 그리고 백그라운드 로직들과 간단한 해결책에 대해서 정리하게 되었다. 이 글을 통해서 안드로이드 개발자들이 다양한 백그라운드 정책이 존재한다는 것을 알게 되었으면 좋겠고, 짧은 시간으로 자신의 앱에 반영했으면 합니다.

 

*2. Doze 모드와 AppStand by의 목적 참고 사이트 - https://developer.android.com/training/monitoring-device-state/doze-standby?hl=ko

*3. Android 7.0에서 도즈모드 - https://developer.android.com/about/versions/nougat/android-7.0-changes

*4. 앱 대기모드 이해 - https://developer.android.com/training/monitoring-device-state/doze-standby?hl=ko#understand_app_standby

5. 백그라운드 실행 제한 - https://developer.android.com/about/versions/oreo/background

6. App Standby buckets - https://www.google.com/url?sa=t&source=web&rct=j&url=https://developer.android.com/topic/performance/appstandby&ved=2ahUKEwjBhoL5l6XmAhW4yosBHZpkAxQQFjABegQIDhAG&usg=AOvVaw2ZNFxungcxxhItfvjDZPa5

7. App Restrictions & Battery saver - https://developer.android.com/about/versions/pie/power?hl=ko

8. 각자 다른 제한상황에서 대처 방법 - https://youtu.be/-7eZL3XRqas

9. 이미지 아이콘 : https://www.flaticon.com/free-icon/smartphone_492155?term=smartphone&page=1&position=10