스마트택배의 타겟 SDK 26 작업기

2018/10/10

스마트택배의 타겟 SDK 26 작업기

안녕하세요. 모바일 팀의 sh입니다.

재작년에 공개된 Android SDK 26의 작업을 스윗트래커에서는 요즘 작업을 하고 있습니다.

Android SDK 26의 관건은 역시 Doze mode 였습니다.

Doze mode는 기기의 화면이 꺼져있고, 충전중이지 않으면 sleep 상태로 빠지는 것을 말합니다.

이러한 경우에는 거의 모든 백그라운드 서비스가 종료된다고 보면 됩니다.

또한 알람매니저에 등록한 task도 정상적이지 않게 작동하게 됩니다 (9분에 하나의 알람만 작동)

시작하기전까지만 해도, 큰 문제점은 없어보였습니다.

Notification Channel 이야 설정해주면 되고, View 관련된 코드들도 그냥 일반적으로 수정해주면 됩니다.

이런 것들은 sdk 26에서도 지극히 마이너한 이슈라고 봐야합니다.

하지만 아예 기능이 빠져버리거나, 제한해버리면 방법이 없습니다.

이걸 우회할 수 있는 방안으로 개발을 해야 합니다.. ㅠㅠ

스마트택배에서는 정말로 많은 것들을 뒤에서 작동 하고 있습니다

택배 조회도 주기적으로 서버와의 통신을 통해서 사용자에게 안내를 해주고 있습니다.

만약 송장번호가 없는 경우에는 쇼핑몰에서 직접 송장번호를 가져오기도 합니다.

배송이 완료되면 착한택배 알람도 띄워줘야 하고요.

이 모든것을 알람매니저 하나로 컨트롤 하고 있습니다.

그런데, sdk 26 알람매니저에 관련하여 안드로이드 공식 문서에 이러한 글이 있네요.

참고: setAndAllowWhileIdle()과 setExactAndAllowWhileIdle() 모두 앱에서 알람을 9분마다 최대 한 번만 실행할 수 있습니다.

흠.. 9분에 하나라..

만약에 송장번호 1,2,3,4,5가 1분 간격으로 등록이 되어있다면, 1번부터 5번까지 전부 다 조회를 하는 시간은 45분이 걸릴겁니다.

헤비유저들의 경우에는 간혹 20~30개의 송장을 조회하는데 이렇게 되면 doze mode에서 정상적으로 조회가 되질 않게 됩니다.

가장 좋은 방법은 서버상에서 먼저 인지를 해서 사용자에게 안내를 해주는 것이지만, 안드로이드 사용자가 워낙 많기 때문에 이러한 부분이 현재는 불가합니다.

만약 10개의 송장번호를 90분에 걸쳐서 알려주게 되면 우리는 스마트택배에서 스마트라는 단어를 뺴야할지도 모르곘습니다.

스마트라는 단어를 유지하기 위해서는 우리는 Doze mode에서도 뭔가를 작동시켜줘야 합니다.

특정 블로그에서 보면 Doze mode에서 GCM 을 이용하여 Doze mode를 깨울 수 있다고 하지만, 실제로 테스트 결과 Doze mode는 깨우는게 아니고 네트워크 access 를

잠깐동안만 획득 할 수 있는 것으로 확인되었습니다. 즉 이건 안될것같습니다..ㅠㅠ

우리는 알람매니저에 등록되는 것들을 분리시킬 필요가 있었습니다.

제일 빈번하고 무작위하게 등록되는 알람 매니저는 다른 방법으로 분리시켰어야 했습니다.

일단 우선순위를 정해야했습니다.

현재 알람매니저를 통해서 등록되는 task 는 다음과 같았습니다.

  1. 배송목록에 등록되는 송장번호가 있는 아이템
  2. 배송이 완료되면 나오는 착한택배 아이템
  3. 배송목록에 등록되는 송장번호가 없는 아이템
  4. wifi + 충전중일때 하루에 한번 작동하는 쇼핑몰 불러오기
  5. 기타 등등

여기서 가장 많이 등록이 되는 것은 1. 배송목록에 등록되는 송장번호가 있는 아이템 이었습니다.

한번에 많개는 수십개의 송장번호가 등록이 되기도 합니다.

굳이 Doze mode 일때 이 친구들은 정확하게 알람 Task가 작동안되도 됩니다.

그리고 이 친구들이 주기적으로 알람 Task을 작동시키면 배터리 소모량은 더 크게 나오기 마련입니다.

그럼 이 친구들은 Doze mode 일때는 가끔씩만 호출이 되면 됩니다.

가장 많이 알람매니저에 등록되는 친구들을 1순위에서 제외하자마자 나머지 알람들은 그냥 그대로 써도 될 것 같습니다.

아무튼간에 송장번호들은 따로 알람 같은 무언가를 통해서 그래도 주기적을 호출은 시캬줘야 할 것 같습니다.

그래서 적용하게 된 부분들이 Firebase JobDisPatcher 입니다.

https://github.com/firebase/firebase-jobdispatcher-android

자세한 설명은 문서상으로 보시면 됩니다.

제가 Firebase JobDisPatcher 를 알람매니저 대신 사용한 이유는 아래와 같습니다.

  1. Doze mode 일때도 maintenance window 환경에서 주기적으로 호출을 해준다.
  2. JobScheduler의 경우 API 21부터 사용이 가능하여 하위 호환성을 고려해야 하지만 요넘은 API Level 이 9이상이라 고민할 필요가 없다. (21 미만은 알람매니저를 활용한다고 하네요.)

알람매니저처럼 Doze mode 일때 9분에 하나씩 호출되는 두려움 없이 사용이 가능한 참 멋진 녀석입니다.

단, 정확하게 설정해놓은 시간에 도는게 아니라서 조금은 아쉽지만 그래도 그 오차범위가 그렇게 크진 않으니 문제없이 사용이 가능합니다.

대신 알람 매니저에서 제공해주는 PendingIntent 의 null 값 유무를 통해서 이미 등록이 되었는지 안되었는지는 개발자가 따로 구현을 해야하는 불편함이 있지만, 이러한 단점을 다 메꿔주고도 장점이 너무나 매력적으로 다가옵니다.

그럼 가장 많은 알람을 걷어내는 일은 다 끝났습니다.

또 한가지! 우리는 생각해보니 송장번호가 없는 아이템의 경우 쇼핑몰을 주기적으로 호출하여 송장번호의 유무를 확인해줘야 합니다.

그런데, 이게 최장 시간이 130초입니다. 즉 2분동안 돌아야합니다.ㅠㅠ

아시다시피 Doze mode의 maintenance window 는 약 40초정도만 유지됩니다.

그럼 나머지 90초에 대해서는 어떻게 확보를 해줘야 할지 고민을 했습니다.

그러던중 음악 스트리밍 서비스처럼 Doze mode 에서도 지속적인 네트워크 연결을 하는 케이스에 대해서 찾아보던 중 해결책은 Foreground Service를 별도의 프로세스로 분리해서 분리하는 거였습니다.

구글 개발자 문서에서는 공식적으로 언급하지 않은 방법이지다만, 된다는 사람들이 많으니 한번 시도해봤습니다.

다행히 쇼핑몰에 접속해서 데이터를 획득하는건 시작과 끝의 시점을 알 수 있습니다.

시작과 동시에 Service를 바인딩해줘서 상태 메세지를 생성해주고, 끝남과 동시에 Service를 stop 시켜주면 됩니다.

자세한 문서는 https://brunch.co.kr/@huewu/3 를 참고하시면 될 것 같습니다.

isDeviceIdleMode() 를 통해 Doze mode 인 경우에만 service를 바인딩 시키고 service class 내에서 startForeground를 시작해주면 됩니다.

이렇게 되면 Service와 BroadcastReceiver와의 통신이 필요한데, 이는 Binder를 상속받은 inner class에서 service class를 반환시켜주고, BroadcastReceiver에서는 ServiceConnection 를 구현하여 onServiceConnected 에서 파라미터로 들어오는 IBinder를 이용하면 service class를 받아서 통신이 가능합니다.

이러한 방법으로 Service class 내에서 인터페이스를 만들고 BroadcastReceiver에서 구현 후 반환 받은 Service에 인터페이스를 꽂아주면 양방향 통신도 가능합니다.

보통 이런 방법으로도 Service <-> Activity 간의 통신도 하고 있습니다.

아무튼 이러한 방법으로 Doze mode 에서도 언제든지 130초간에 기능을 수행 할 수 있는 구조들이 만들어졌습니다.

실제 테스트 결과에서도 아주 만족스러운 결과를 가져왔고, BroadcastReceiver를 많이 제거하고 Firebase JobDisPatcher를 사용함으로인해서 배터리 효율성에 있어서도 크게 늘어날 것으로 보고 있습니다.

10월 말까지 충분한 테스트를 마치고 스마트택배는 업데이트를 예정할 생각입니다.

스마트택배처럼 수 많은 알람들로 인하여 고민하고 있는 개발자들에게 큰 도움이 되길 바랍니다.

감사합니다.

Post Directory