Android WorkManager 를 이용하여 매일 알림 표시하기

김종식
원티드랩 기술 블로그
6 min readSep 7, 2021

원티드 안드로이드앱 7.1.0 부터 매일 오전 8~9시 사이에 사용자가 놓친 알림이 있을 경우 확인을 유도하는 노티피케이션이 표시되고, 사용자는 이 알림을 선택하면 앱 내 알림센터로 이동하게 되는 기능이 추가되었습니다.

놓친 알림이 있는데 확인 부탁드려요 ~

안드로이드에서 앱의 실행 및 종료 여부와 상관없이 수행되어야 하는 백그라운드 작업을 Service, AlarmManager나 BroadcastReceiver 를 사용, JobScheduler 를 이용하여 처리가 가능했습니다. 하지만 이러한 백그라운드 작업은 디바이스 메모리나 베터리 등 하드웨어 작업을 많이 소모하게 되었고, 안드로이드 M 부터 Doze mode, App standby, Limited implict broadcasts, Background restricted apps 등 백그라운드 작업에 제한을 두는 정책들이 생겨나면서 백그라운드 작업을 실행하는 것이 점점 더 어려워졌습니다.

WorkManager는 Google I/O 2018에서 공개되었으며, Android Jetpack 의 아키텍처 구성요소 중 하나로 아래와 같은 특징이 있습니다.

  • 제약 조건을 가지고 실행할 수 있으며, 그 실행이 보장됩니다.
  • 장치의 상태를 존중하면서 실행됩니다. (예를들어, doze mode 에서 작업을 처리하기 위해 기기를 깨우거나 하지 않습니다.)
  • 구글 서비스 유무와 관계없이 동작됩니다.
  • 대기, 실행, 완료 등 상태 조회가 가능합니다.
  • 작업의 연결 처리가 가능합니다.
  • 제한 조건이 충족 되었을 때 즉시 실행됩니다.

WorkManager 를 이용하여 매일 알림을 표시하는 작업 내용을 정리해 보았습니다.

매일 알림 표시하기

매일 읽지 않은 알림이 표시되는 조건은 다음과 같습니다.

  1. 하루에 한번, 매일 아침 8~9시 사이에 표시된다. (서버 부하가 걸리지 않도록 8~9시 사이 10분간격 랜덤)
  2. 만약 사용자가 추가적으로 확인할 알림이 없다면 표시되지 않는다.

언뜻 조건을 보면, WorkManager 에 WorkRequest 설정 방법PeriodicWork 가 적절할 거 같으나 WorkManager 에 특정 시간으로 요청하는 것은 적절하지 않습니다. 앱에서 주기적인 워커를 언제부터 실행시킬 지 계산을 시작하는 것을 알 수 없고(예를들어, 언제 로그인 될 지 모르고), 하루단위로 설정하면 날이 지날수록 베터리 절약 메커니즘으로 인하여 실제 실행되는 시간이 딜레이 될 수 있습니다.

특정 시간에 실행시키는 것은 OneTimeWorkRequest 에 setInitialDelay 를 활용하고, 딜레이 시간을 계산하는 로직을 반복적으로 수행해서 WorkManager 에등록하도록 하며, 이미 존재할 경우에는 replace 옵션을 두었습니다. 전체 소스코드는 아래와 같습니다.

원티드 안드로이드에서 DI 도구로 hilt 를 사용하고 있어, WorkManager 객체 생성 시 의존 객체 주입을 Hilt 를 적용하였습니다. 공식 문서를 보고 처음에 적용했을 때 constructor inject 이 되지 않는 현상을 겪을 수 있습니다. 이 때에는 Android Studio 의 Invalidate Caches / Restart 를 실행하면 정상적으로 동작하는것을 확인할 수 있습니다.

하루에 한번만 테스트 하지 않습니다.

Worker 가 실행될 때 필요한 객체는 아래와 같습니다.

  1. 오늘 이미 표시가 되었는지 여부를 저장하는 Preference
  2. Worker 를 특정 시간에 실행시켜 정상적으로 동작하는지 테스트 해볼 수 있도록 개발용 데이터를 저장하는 Preference
  3. Worker 가 실행될 때 사용자에게 신규 알림이 존재 여부 확인을 위한 Repository
  4. 안드로이드 Notification 표시 기능 중 매일 알림 확인 요청 표시 구현체

Worker는 수행 결과로 Result 를 전달해야 하는데, 만약 오류가 발생할 경우 Retry 결과를 전달하여 Worker 를 재 실행하도록 하고, 3회 실행에도 오류가 발생되면 종료처리하는 예외 케이스에 대한 추가 처리가 있습니다. (runAttemptCount 는 Worker 내부의 WorkerParamters 프로퍼티에서 관리되는 값입니다.)

companion object 에 선언한 runAt 함수는 doWork 에서 수행해야할 로직을 모두 실행한 뒤 다음 실행 시간 계산 및 작업 추가를 위해 호출됩니다.
Worker 외부에서 호출되는 경우는 사용자 인증 성공(로그인/회원가입 등) 또는 개발자 도구에서 표시 시간을 변경할 경우 호출하며, 취소 요청은 인증해제(로그아웃/회원탈퇴 등)성공 시 호출합니다.

마치며

백그라운드 작업 실행을 할 수 있는 다양한 방식들 대비 WorkManager 는 생각보다 단순하며, 훨씬 사용하기 편리했습니다. 여기에 의존 객체를 주입하는데 Hilt 를 활용함으로써 비동기 처리를 위한 공통 로직을 재활용 할 수도 있어 여러모로 유용했습니다. WorkManager 를 좀 더 다양하게 사용해 제품에 적용해보면 좋을 거 같다는 생각이 들었습니다.

원티드에서는 다양한 직군에서 적극적으로 채용중입니다! 서버, 웹, 앱, 디자인 등 제품을 만들어가는 각자의 분야에서 전문적인 분들과 함께 일하기를 기대하고 있습니다. 회사 채용 정보 페이지를 확인해 주세요!

--

--