안드로이드 잡스케줄러 분석

지난 글에서 안드로이드 잡스케줄러를 이용해 백그라운드 작업을 처리하는 잡서비스 생성 시 주의할 점을 살펴보았다.

잡스케줄러 기능은 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분보다 길어지는 것은 아니다. 또한, 단말 여건이 좋지 않을 때 주기가 길어지는 것은 어느 경우든 마찬가지다.
  • 잡서비스 로그 분석은 이 글을 참고하자.

참고

“안드로이드 잡스케줄러 분석”에 대한 2개의 생각

  1. 안녕하세요 혹시 테스트 하신 코드도 공개해주실수 있을까요. 저도 한번 돌려보고 싶어서요.

    1. 앱을 개발하는 과정에서 기능 확인을 위해 테스트한 거라, 테스트 코드가 따로 있지는 않네요 ㅠ

댓글 남기기

이메일은 공개되지 않습니다.

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.