지난 글에서 안드로이드 잡스케줄러를 이용해 백그라운드 작업을 처리하는 잡서비스 생성 시 주의할 점을 살펴보았다.
잡스케줄러 기능은 L(API21)부터 추가되었지만, M(API23)까지는 거의 사용되지 않았다. 안드로이드에서 백그라운드 작업에 애용되던 CONNECTIVITY_ACTION 수신 브로드캐스트를 targetSdk 24(N)부터 사용할 수 없도록 제한하고, 대안으로 잡스케줄을 제안하면서 많이 사용되고 있다.
이번 글에서는 잡스케줄러가 단말에서 어떻게 동작하는지 O OS 기반 테스트를 통해 살펴보자.
O OS를 선택한 이유는?
안드로이드 개발 문서는 잡스케줄의 동작에 대해 상당히 두루뭉술하게 설명하고 있다. 이를 요약하면,
- 최대한 주기에 맞춰 실행시켜 준다.
- 최대한 실행 시간을 보장한다.
이다. 내 생각에 이는 잡스케줄 서비스에 대한 설명이 맞지만, OS 간 동작에 있어 차이가 커 개발자에게는 충분치 않다. 그럼, M OS와 O OS 사이의 차이점들을 살펴보자.
잡서비스에 잡을 등록하면,
- M OS에서는 보통 잡서비스가 곧(몇 분 이내) 실행된다. O OS에서는 보통 즉시(수 초 내) 실행된다.
- 앱을 강제종료(am force-stop)하거나 초기화(pm clear)하면 M OS에서는 잡이 유지되지만, O OS에서는 잡이 사라진다.
위와 같은 OS간 동작의 차이로 인해, 모든 OS(L ~ O)의 공통된 특징을 추리기가 어렵다. 그러므로, 여기서는 앞으로 대세가 될 O OS 대상으로 확인하겠다.
얼마나 자주 그리고 오래 실행되나?
안정적인 백그라운드 작업을 위해서는 자주, 오랫동안 실행하는 것이 중요하다. 확인을 위해 총 4가지의 실행 조건(condition) 만들고 비교 테스트 보자.
대부분의 백그라운드 작업이 긴 시간을 필요하므로, 1회 실행되는 잡을 1개, (각각 다른 방법으로 구현된) 반복 실행 잡을 3개 넣었다.
- C1 = 1회 실행
setOverrideDeadline()을 0ms로 설정 - C2 = 반복 실행
setPeriodic()을 15분으로 설정 - C3 = 반복 실행
최초 setOverrideDeadline()을 0ms로 설정, 잡 서비스의 onStopJob()에서 다음 작업을 반복적으로 등록 - C4 = 반복 실행
최초 setOverrideDeadline()을 0ms로 설정, onStopJob()에서 true를 반환하여 back-off 전략에 따라(기본값 30s) 계속 재 실행
각각의 조건을 O OS 단말에서 실행하면 아래와 같은 그래프가 나온다. (실제 테스트 결과를 기반으로 그린 그래프지만, 수 많은 실행 횟수를 모두 그래프로 옮기기 어려워 임의대로 횟수를 솎아낸 후 표현했다. 실행이 집중되는 시기와 주기 변동같은 특징은 최대한 그대로 옮겼으니 참고 바란다.)
- C1 : 1회성 작업이므로 1회 종료 후 더이상 실행되지 않는다.
- C2 : setPeriodic()에 설정한 값에 따라 주기적으로 실행된다.
- C3 : onStopJob()에서 setOverrideDeadline(0) 설정으로 후속 작업을 등록한다. 그 결과, 작업이 계속 실행되는 것처럼 보이는 경우도 있다. 하지만, 수면 시간이나 다른 작업으로 바쁠 때는 C2와 마찬가지로 주기가 길어진다.
- C4 : back-off 정책에 따라 계속 잡서비스가 계속 재 실행된다. 물론, back-off 정책에 따라 주기가 점점 길어진다.
위 테스트를 기반으로 잡서비스가 실행되는 방식을 정리하면,
- 보통 주기에 맞춰 실행한다.
- 보통 10분간 실행한다.
시스템이 바쁘거나 열악하지 않다면 10분 정도 실행해준다. - 최대 10분을 넘지 않는다.
시스템 상황이 좋더라도, 정확히 10분만 실행한다. - 잡 서비스는 언제든 종료될 수 있다.
시스템 상황이 여의치 않으면 더 빨리(4-5분?) 종료될 수 있고, 시스템 상황이 열악하다면 컴포넌트가 생성되자마자 onStartJob ()이 호출 되기도 전에 종료되기도 한다. - 내가 지정한 실행 주기보다 더 띄엄띄엄 실행될 수 있다.
작업의 재실행 주기를 최소 단위인 15분으로 한 경우에도, 핸드폰이 doze 모드에 들어간다면 더 긴 주기(몇 시간 주기)로 실행될 수 있다.
실행이 보장되나?
백그라운드 작업은 오랜 실행시간 만큼이나, 안정적인 실행도 중요하다. 안정적으로 실행되기 위해서는 내가 등록한 잡이 사라지지 않아야 할 것이다. 이를 확인하기 위해, 위에서 열거한 조건(C1~C4)에 추가적인 테스트(Test)를 해보자.
테스트는 총 7개로, 정상 종료 1개와 나름 위험한(?) 테스트 6개가 포함된다.
- T1 = 정상종료
안드로이드 시스템이 잡 서비스에 허락한 시간을 모두 동작한 후 종료되었다. - T2 = 업데이트 버전 재설치
- T3 = 업데이트 버전 삭제
- T4 = 강제 종료
e.g. LMK에 의한 프로세스 종료, ‘am force-stop <패키지 이름>’, 안드로이드 스투디오 내 로그캣 패널의 ‘terminate application’ 버튼 - T5 = 앱 초기화
e.g. ‘pm clear <패키지 이름>’, 단말의 ‘설정-어플리케이션-내 앱-저장공간-데이터삭제’ 메뉴 - T6 = JobInfo.setPersisted(true) 설정, 실행 중 재부팅
- T7 = JobInfo.setPersisted(false) 설정, 실행 중 재부팅
위와 같은 테스트 항목들을 조건들(C1 ~ C4)에 실행한 결과는 아래와 같다.
잡이 유지되는 경우는 ‘O’, 제거되는 경우는 ‘GONE’로 표시했다. 잡이 사라지는(GONE) 경우가 많아 보이지만,
- T3 : 대부분의 앱은 프리로드 앱이 아니다.
- T2, T4~T7 : 자주 발생하는 상황이 아니다.
그러므로, 앱이 정상구동한다면 잡은 대부분 유지될 것이다. 위 테스트를 기반으로 잡이 유지되는 조건을 정리하면,
- setPeriodic()을 설정한 잡이 정상종료 한 경우
- onStopJob()에서 true를 반환 후 정상종료 한 경우
- 정상 실행 중, 단말이 재부팅되는 경우
이다. 다만, 잡이 예기치 않게 제거될 수 있으니 앱이 실행될 때마다 잡이 잘 등록되어 있는지 확인이 해야할 필요가 있다.
그 밖에
- 잡이 실행 중일 때, 같은 아이디를 가진 잡을 재등록하면 기존 잡은 종료되고 새로 등록한 잡이 바로 실행된다.
- onStartJob()에서 false를 반환하는 경우, onStopJob()을 호출하지 않고 서비스 컴포넌트가 종료된다. onStopJob()이 호출되지 않으므로 back-off 정책에 따른 재실행 요청을 할 수 없다.
- 단말의 여건이 좋을 때는 C2보다 C3가 더 자주 실행된다. 하지만, 잡 서비스가 살아있는 시간이 늘어나는 것이 아니라, 종료 후 바로 재실행되는 것이므로 실행 시간이 10분보다 길어지는 것은 아니다. 또한, 단말 여건이 좋지 않을 때 주기가 길어지는 것은 어느 경우든 마찬가지다.
- 잡서비스 로그 분석은 이 글을 참고하자.
참고
안녕하세요 혹시 테스트 하신 코드도 공개해주실수 있을까요. 저도 한번 돌려보고 싶어서요.
앱을 개발하는 과정에서 기능 확인을 위해 테스트한 거라, 테스트 코드가 따로 있지는 않네요 ㅠ