본문 바로가기

Android

WorkManager

목표

내일 실행될 Reminder전날에 등록하는 백그라운드 작업을 실행하는 것.
오늘 자정이 지나가기전 내일 사용자에게 알려줘야할 Notification들을 시스템에 등록하는 작업을 실행해야 한다.

  • 특정한 시간 이전까지 실행되어야 한다.
  • 실행이 되지 않을 경우 Reminder가 누락될 수 있다. 실행이 보장되어야 한다.

 

안드로이드의 백그라운드 작업

안드로이드의 백그라운드 작업은 AlarmManager를 시작으로 JobScheduler등을 거쳐서 최신 WorkManager까지 다양한 라이브러리가 공존한다.

 

다양한 플랫폼, API 버전등을 포괄해서 지원하는 최신 라이브러리인 WorkManager를 사용해보는 것은 이번 프로젝트의 목표 중에 하나였다.

 

사용하기에 앞서 WorkManager를 간단히 살펴보자.

 

WorkManager

WorkManager는 백그라운드 작업을 실행하는 Framework이다. 디바이스의 환경에 맞는 API를 선택해서 백그라운드 작업을 실행하는 주체이다.

 

내가 실행하고 싶은 백그라운드 작업을 기술하는 공간은 Worker라고 한다.

public class TestWorker extends Worker {
    public TaskWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        // 생성자
        super(context, workerParams);
    }

    @Override
    public ListenableWorker.Result doWork() {
        // 원하는 백그라운드 작업을 수행하는 메소드.
        // BroadcastReceiver의 onReceive() 메소드와 비슷한 역할.
    }
}

doWork() 메소드에서 백그라운드 작업을 실행하는데. 반환 값이 ListenableWorker.Result이다.

 

Result 클래스는 Success, Failure, Retry 3가지의 State 값을 가지는 클래스이다.
Success를 반환하면 백그라운드 작업이 성공적으로 마치고 끝내고 상태가 변경되는 것이고,
Retry의 경우 제한조건을 만족하지 못한 경우 등에서 잠시 후에 다시 백그라운드 작업을 시도하는 것이다.
Failure는 작업 실패를 한 경우이다.

 

LiveData를 통해서 백그라운드의 작업 현황과 State상태를 구독해서 확인할 수 있는 장점이 있다.

 

 

그 다음 살펴볼 것은 WorkRequest이다.
Intent에 내용을 담아서 PendingIntent로 만든 후 시스템에 등록하면 브로드캐스트가 실행되는 것을 떠올려보자.

WorkRequest는 PendingIntent와 유사한 역할이다. 실행할 백그라운드 작업을 가지고 있는 Worker를 WorkRequst에 첨부한다.

 

그리고 WorkManager의 큐에 WorkRequest를 첨부하는 것이다. 이때 WorkManager의 가장 큰 특징인 제약조건을 설정할 수 있다.


제약조건으로는 배터리가 충전 중일때, 네트워크에 연결되었을때 등 여러가지 옵션이 있다.

이 옵션이 충족되었을 때만 WorkManager가 백그라운드 작업을 수행하게 된다.

 

이러한 옵션들은 WorkManager의 특징을 보여준다.

안드로이드 Oreo(8.0)+ 버전부터 배터리 효율개선을 위해서 백그라운드 작업에 대한 정책이 매우 엄격해졌다.
그래서 기존에 네트워크 연결 상태를 체크하는 등의 용도로 사용되었던 Implicit Recevier들을 사용하지 못하게 제한했고, WorkManager를 통해서 사용하도록 가이드 라인을 지정했다.

 

또 배터리 효율을 위해서 Doze모드와 App StandBy모드 같은 Idle상태가 도입되었는데. 이 때는 사용자가 스마트폰을 사용하고 있지 않은 상태이므로 백그라운드 작업이 연기될 수 있다.

 

요약하자면

WorkManager의 특징은 다음과 같이 요약할 수 있다.

  • 큐에 입력한 백그라운드 작업은 실행이 보장된다. (재부팅에도 살아남는다. 내부적으로 DB에 작업저장해두기 때문이다.)
  • Idle 상태에서 실행이 지연될 수 있다.
  • 그래서 정확한 시간에 작업을 실행 시키는 것은 불가능하다.
  • 여러 종류의 백그라운드 작업 API를 모두 포괄해서 호환하므로 디바이스에 맞는 API를 내부적으로 선택해준다.

 

WorkManager는 작업을 얼마나 지연시킬까?

우리의 목표는 다음날이 되기 전에 다음 날의 Reminder Task를 Notification 큐에 등록시키는 작업이다. 그러므로 특정시간(오늘 내에) 이전에 작업이 반드시 수행되어야 한다.

 

구글에서 설명하는 백그라운드 작업을 위해 어떤 라이브러리를 사용해야하는가에 대한 가이드라인이다.

우리는 Needs to executre at an exact time가 아니라 until at a specific time에 실행이 보장되어야한다.

 

하지만 시간에 관한 조건을 부합시키는 라이브러리는 현재 AlarmManager 뿐이다. 그러므로 AlarmManager와 BroadcastReceiver 조합을 사용해서 구현해야할 것이다.

 

BroadcastReceiver의 문제점

  • 디바이스 재부팅 시 등록된 Broadcast가 초기화된다.
  • 앱이 Crash 난 경우 Broadcast가 소실될 수 있다.
  • 한번 소실이 난 경우 매일 반복실행되어야할 백그라운드 작업의 루프가 끊긴다.

 

두 가지의 장점 모두를 사용할 방법이 없을까?

첫번째 상상. Broadcast in Worker.

Worker의 백그라운드 잡으로 AlarmManager를 통해서 자정에 Reminder등록 백그라운드 작업을 실행예약 시킨다고 생각해보자.
Worker로 인해 Loop가 끊기는 것을 방지할 수 있으며, AlarmManager를 이용해서 자정이라는 특정 시간에 작업을 실행시킬 수 있을 것이다.

 

하지만 Worker가 Alarm을 등록한 뒤 실행되기 전에 디바이스가 재부팅된다면. 예약된 Alarm이 사라지고 그 날은 Reminder 등록 작업이 생략될 것이다. 하지만 Worker를 이용하면 Loop는 유지되어서 다음날에는 다시 Reminder 등록 작업이 예약될 것이다.

 

 

 

두번째 상상. Worker in Broadcast

이번에는 반대로 Broadcast가 특정시간에 실행되어서 Worker가 백그라운드 작업(Reminder 등록)을 실행하는 방법이다.
특정시간에 백그라운드 작업 자체가 트리거 된다는 점에서 이번 구조가 더 매끄러워 보인다.

 

하지만 역시 Reboot 상황에서 Broadcast가 소실될 위험이 있고, 이 경우에는 Loop 자체가 깨지게 된다.

 

만약 우리가 실행해야될 작업이 Network를 필요로 하거나 충전중일때만 진행할 것과 같은 제약조건이 붙는 작업.
예를 들어 서버와 데이터를 sync하는 백그라운드 작업이라면 사용해볼만한 구조인 것 같다.


sync는 한 번 빼먹어도(제약조건이 충족되지 않았거나, WorkManager가 작업지연을 시켰거나) 큰 문제가 발생하지 않을 가능성이 높으며, 제약조건을 사용하기 위해 Worker를 사용하면서도 Broadcast를 사용해서 특정 시간에 작업을 시작시킬 수 있다.

 

 

사실 Reboot가 일으키는 문제는 "android.intent.action.BOOT_COMPLETED"를 받는 Receiver를 사용하면 보완할 수 있다.
하지만 이런 보완재를 사용하는 것은 Worker 없이 AlarmManager, BroadcastReceiver만 사용하더라도 충분히 같은 동작을 보장할 수 있을 것이다.

 

WorkManager로 우선 적용을 해보자.

사실 검색해보니 특정시간에 백그라운드 작업을 특정 주기 반복 실행하는 방법은 생각보다 명쾌한 솔루션이 있지 않은 듯 했다.
서버와 통신을 하는 앱의 경우 서버로 부터 신호를 받아서 백그라운드 작업을 하는 것이 최선으로 생각된다.

 

초기에 프로젝트 목적에 WorkManager를 사용해보는 것이 있었으므로
BroadcastReceiver가 아닌 WorkManager를 이용해서 우선 구현해보도록 한다.

지연이 많이 발생해서 사용할 수 없을정도라면 AlarmManager를 이용해서 다시 구현하도록 하자.

Summary

특정 시간에 백그라운드 작업을 실행하려면 AlarmManager가 최선이다.
그 이외의 백그라운드 작업은 WorkManager가 갑이다.

하지만 WorkManager를 이용해서 Reminder 등록을 구현해볼 예정이다.