읽기전 주의) 실험적인 기능으로 장기 사용 등에 대한 제한은 아직 테스트되지 않았습니다 🥲
안드로이드는 Service를 통해 백그라운드 작업을 수행할 수 있다.
물론 Coroutine, Thread 등의 백그라운드 스레드를 통해서 백그라운드 작업처럼 보이게 수행가능하지만, activity 등의 시작점이 되는 컴포넌트가 필요하고, lifecycle 관리도 별도로 해주어야 한다.
WorkManager라는 선택지도 있지만, 짧고 정확한 시간마다 동작해야한다라는 요구사항이 있었다. (Workmanager는 최소 15분 텀에 작업 우선순위에 따라 지연될 수 있다.)
결론부터 이야기 하겠다.
Foreground Service + AlarmManager
위에서 이야기 했듯이 백그라운드 작업을 위해서 Service는 필요하다. 그 중에서도 Foreground Service를 선택했는데, 가장 주된 이유는 알람을 표시하여 지속적인 백그라운드 작업이 가능하다는 것이다.
추가 정보) 백그라운드 서비스나 바운드 서비스인 경우 작업이 완료되면 서비스는 종료된다.
또한 AlarmManager를 사용하면 setAlarmClock() 함수를 통해 doze 모드를 무시하고 정확한 알람을 설정할 수 있다.
서비스를 지속적으로 살리는 로직의 핵심은 아래와 같다.
- 알림을 지속적으로 설정한다. 필자는 Alarm을 받는 Receiver에서 알림을 다시 설정하는 방식, 재귀 방식을 택했다.
- 알림을 받는 Broadcast Receiver에서 (서비스가 죽어 있을 시) 서비스를 실행한다.
- Service에서 지속적인 작업을 수행한다. (필자는 블루투스 작업 수행)
manifest에서 Broadcast Receiver 액션으로 BOOT_COMPLETED 설정을 해주면 부팅시 자동으로 receive 메소드가 수행된다.
3가지 요구사항을 충족시켰다.
1. 프로세스가 종료될 시 서비스 재시작
2. 재부팅시 서비스 재시작
3. 포그라운드, 백그라운드 전환간 자연스러운 측정 진행
블루투스 사용시에 연결해제가 잘 안되는 버그가 있었다. 블루투스 연결해제 이벤트가 들어왔음에도 기기는 연결해제 이벤트가 들어오지 않은 현상이 있었다. 명확한 원인은 아직 파악이 안되어서 우선 기기 연결해제는 peripheral에서 주도하도록 하여 해결하였다.
현재 지속적으로 BLE를 연결하기 위한 로직을 두고 있지만, 사실 사용자 경험으로는 그렇게 좋은 로직은 아닌 것 같다. 안드로이드 시스템적으로 사용자의 전력을 많이 잡아먹으면 프로세스가 강제로 종료될 수 있으며, 내가 의도한 로직이 정상적으로 작동 안할 수 있다. (앱 대기 버킷 등의 이유)
프로세스가 자동으로 살아나는 로직을 구현해보았다. 어려웠던 부분은 포그라운드, 블루투스, AlarmManager간 정책들이 얽히고설켜있어 제약사항을 정리하는게 가장 어려웠던 것 같다.
또한 이런 정책들이 있는게 운영체제 정책상으로 불멸의 서비스는 의도와 다른 설계라는 생각이 많이 들었던 것 같다.
CompanionDeviceService로 위 구현을 커버할 수 있지만 Android 12에서 정상적인 작동을 안하더라.. 만약 BLE Immortal 기능을 원한다면 CompanionDeviceService를 지켜보자.