👩🏻‍💻 TIL

만보기와 안드로이드 배터리 최적화 관련 분석

ji-hyun 2024. 4. 28. 21:53

우리 회사에서는 만보기 서비스를 메인으로 하여 어플을 운영하고 있다.

안드로이드 만보기는 지난 1년간 문제 없이 동작했었는데 갑자기 근 몇달 간 만보기 관련 CS 가 쏟아지기 시작했다.

아무래도 안드로이드 OS 가 업데이트 되면서 백그라운드 작업에 대한 제한이 강화된 것으로 추측한다.

(최근 업데이트 내용을 보면 백그라운드 관련 내용이 자주 언급되었다)

 

 

 

안드로이드 만보기는 백그라운드를 바탕으로 철저히 돌아가는 서비스이다.

근데 백그라운드 작업을 제한한다고..? 제대로 서비스를 유지할 수 없을 것이다.

 

 

 

나는 아이폰 사용자였지만, 이 문제를 해결하기 위해 며칠 간 안드로이드 타사 만보기 앱을 사용해 보았다. 그리고 다른 앱들이 백그라운드 제한을 어떻게 극복했는지 분석할 기회를 가졌다. 이번 글에서는 그 분석을 바탕으로 내가 이해한 내용을 정리해보려고 한다.

(참고로 내 뇌피셜이니 참고만 하기!)

 

 

 

 

 

 

 

 

만보기 CS 내용

 

처음 CS 를 마주하게 된 건 삼성 품질팀에서 CPU 사용량이 크다고 연락이 왔다. 

구글에 찾아봐도 삼성 품질팀에서 연락이 오는 경우가 잘 없는 것 같은데...

삼성 품질팀은 고객의 폰이 발열이 난다고 하여 분석해보니 아마 우리 어플이 문제 된 듯하다.

 

 

CPU 사용량 수치를 보니 웬만한 게임 고사양 어플보다 우리 어플 CPU 사용량이 컸었다.

그때 당시 왜 그런지 이해할 수 없었지만 지금에서야 이해된건 가속도 센서(흔들림을 간지하여 걸음수 측정, 걸음수를 바로 보이기 위해선 민감도를 최대로 높여야 한다) 사용 관련이 하드웨어 배터리 소모를 촉진시키고 이에 더불어 지속적인 백그라운드 작업을 하기 때문에 고객의 폰이 발열이 일어난 것이 아닐까 추측 된다.

 

 

크게 보면 위와 같은 이유이긴 한데 우리 어플 서비스 중에서도 추가로 히스토리 내역을 위해 앱을 사용하지 않을 때에도 지속적으로 걸음수를 서버에 보내고 매일 자정에 초기화하기 위해 타이머도 지속적으로 돌리고 있는 것도 원인이 될 수 있을 것 같다.

 

 

그때 당시 이 문제를 해결한답시고 프로파일러 돌려서 앱의 모든 이미지를 캐싱 처리했다.

만보기 기능을 수정할 수 있는 건 없었다.. 기획상 모두 필요한 구현이었다.

 

 

기억나는 첫 CS 는 위와 같은 건이었고 이후에도 꾸준히 유사하게 배터리 광탈 CS 건, 걸음수 초기화되는 이슈건 등이 있어 우리 앱은 CPU 사용량 및 배터리 소모량이 크구나 를 인지하고 있었다.

(참고로 걸음수 초기화되는 이슈건은 백그라운드에서 돌다가 배터리 문제로 앱이 죽어버린 이슈와 관련이 있다)

 

 

 

 

 

 

 

 

안드로이드 걸음수는 어떻게 측정될까?

대부분 안드로이드 만보기 어플들의 걸음수 측정은 어떻게 이루어질까?

 

 

안드로이드 가속도 센서는 말 그대로 흔들림을 감지하여 걸음수를 측정해주는데 그게 오늘 하루만의 걸음수를 측정해주지 않는다.

그래서 개발자는 오늘 걸음수를 가져오기 위해 복잡한 계산을 거쳐야 한다.

넘어야 할 관문은 걸음수가 갑자기 줄어들었는지, 사용자가 단말기를 리셋하였는지, 자정이 넘어갔는지, 이전에도 사용중이었던건지 등 여러가지를 계산해야 한다.

 

 

 

근데 이를 체크하려면 백그라운드가 지속적으로 돌아야 한다.

초반엔 위 기능이 잘 돌아갔는데 안드로이드 OS 가 최근 업데이트되면서 백그라운드가 잘 돌아가지 않았다.. (내 주변 안드로이드 유저들은 특히 이번에 대대적인 OS 업데이트가 있었다고 한다)

 

 

 

 

 

 

 

 

안드로이드 OS 업데이트 내용 (현재 14 기준)

현재 최근 OS 버전은 14이다.

처음 개발할 당시 최근 버전은 12 쯔음이었던 것 같다. (12가 나왔지만 12 이전 버전들도 간간히 있는 상태..?)

나는 평소에 아이폰을 주로 사용하고 있고 어쩌다 안드로이드 특정 기능 개발 시, 잠깐 안드로이드 폰을 사용해봤기 때문에 업데이트 히스토리를 잘 몰랐다.

 

 

그래서 어떤 업데이트 히스토리들이 있었는지 블로그 글과 공식 문서를 많이 찾아봤다.

정보들을 찾아보면서 전반적으로 느꼈던 것은 최근 들어 안드로이드가 배터리를 중요하게 관리한다는 느낌이었다.

주요 업데이트가 몇 가지가 있는데 한 번 아래에 나열해보겠다.

 

 

 

 

1. 안드로이드 13 (API 레벨 33) 부터 사용자가 포그라운드 서비스와 연결된 알림을 즉시 닫을 수 있다.

 

즉 알림탭에서 스와이프하여 알림을 지울 수 있다는 뜻이다.

기존에는 알림탭에서 포그라운드 서비스 관련 알림을 스와이프해서 지울 수 없었으나 안드로이드 13부터 변경되었다.

내 테스트 폰이 12 버전이었는데 최근 14 로 업데이트 후 알림탭 알림이 지워져 버려서 당황했었다.

(알림이 지워져서 만보기 실행이 안되는 것 아닐까 생각했지만 공식 문서에는 다행히 알림탭 알림만 지워지는 것 뿐이라고 써져 있었다.)

 

https://developer.android.com/develop/background-work/services/foreground-services?hl=ko#user-dismiss-notification

 

 

 

 

2. 백그라운드에서 실행되는 동안 포그라운드 서비스를 시작할 수 없음 (안드로이드 12 & API 레벨 31)

 

이 건은 아마 안드로이드가 배터리를 효과적으로 관리하려는 작업 중 하나이지 않을까 생각된다.

(찾아보면 다수가 이 문제에 대해 골머리 앓는 듯하다)

 

 

https://developer.android.com/about/versions/12/behavior-changes-12?hl=ko#foreground-service-launch-restrictions

-> 안드로이드 12 (API 레벨 31) 이상에서는 백그라운드에서 실행되는 동안 포그라운드 서비스를 시작하는 것에 대한 제한이 강화되었다는 내용

 

 

위 내용을 내가 이해한 바로는 지속적인 백그라운드 작업을 위해 포그라운드 서비스로 띄워주고 있었는데 배터리 소모 문제로 인해 안드로이드 OS 가 포그라운드 서비스를 kill 해버리고 백그라운드에서는 다시 포그라운드 서비스를 시작하려는데 이때 제한될 수 있다는 내용인 것 같다.

이 경우 포그라운드 서비스를 시작하려고 하면 ForegroundServiceStartNotAllowedException 이 발생한다.

 

android.app.ForegroundServiceStartNotAllowedException - Service.startForeground() not allowed due to mAllowStartForeground false:

 

 

물론 일부 예외적인 경우에 백그라운드에서 포그라운드 서비스를 시작할 수 있긴 하다.

(아래를 클릭하면 예외적인 사례인지 확인해 볼 수 있다!)

더보기

 

 

만보기 같은 경우에는 위에서 나열된 것 중 activity recognition transition 이벤트를 받는 것에 해당하기 때문에 우리 앱은 포그라운드 시작 제한 예외가 아닌데 위 오류가 발생하고 있는 중이다... 이 오류에 대해선 좀 더 우리 코드를 분석해봐야겠다.

 

 

 

 

아무튼 위에서도 언급했지만 이 업데이트 내용은 구글이 안드로이드 OS 의 배터리 관련하여 중요하게 다루는 이유와 관련이 있는 것 같다.

왜냐하면 포그라운드 서비스는 사용자에게 지속적으로 보이는 알림을 통해 실행 중임을 알리고, 이를 통해 앱이 백그라운드에서 자동으로 종료되는 것을 방지하기 때문이다. 이러한 특성은 포그라운드 서비스가 메모리에서 지우기 어렵게 만들어 시스템 리소스를 많이 사용할 있도록 할 수 있다.

 

 

ChatGPT 에게 좀 더 자세하게 "백그라운드에서 포그라운드 서비스 시작 제한의 이유" 에 대해 물어보았을 때도 역시 배터리 관련하여 답하는 것 같다.

 

1. 개인 정보 보호 강화: 사용자가 알지 못하는 사이에 백그라운드에서 실행되는 앱이 계속해서 자원을 사용하고, 사용자의 장치에 영향을 줄 수 있습니다. 사용자에게 명확하게 알리지 않고 포그라운드 서비스를 실행하는 것은 사용자의 동의 없이 개인 정보를 사용할 수 있는 여지를 제공할 수 있습니다.

2. 배터리 수명 개선: 백그라운드 앱이 포그라운드 서비스를 자주 시작하면, 이는 장치의 배터리를 빠르게 소모할 수 있습니다. 이를 제한함으로써 안드로이드는 배터리 수명을 늘리고 사용자 경험을 향상시키려고 합니다.

3. 장치 성능 향상: 백그라운드에서 실행되는 앱들이 포그라운드 서비스를 자주 시작하게 되면 시스템 자원을 과도하게 사용하여 전체적인 장치 성능이 저하될 수 있습니다. 특히 메모리와 CPU 사용량이 증가하게 됩니다.

 

 

 

 

 

 

 

 

타사 만보기 어플 권한 비교 분석

타사 만보기 어플들은 백그라운드 제한을 어떻게 해결했을까?

앱을 실행하면 유저의 권한 여부를 묻게 되는 부분을 중점으로 분석해보았다.

 

 

 

1. 베터리 사용량 최적화 중지 권한 

- 만보기 어플 모두 사용하는 권한

 

먼저 만보기 어플들 모두 이 권한을 사용하는 것 같았다.

역시 OS 에 의해 죽지 않는 백그라운드 서비스를 만드려면 배터리 최적화 거부 권한이 절대적으로 필수인 것 같다.

또한 모든 어플의 만보기 기능 관련 문의사항에서 배터리 제한 없음으로 설정해달라는 문구가 항상 있었다.

 

 

<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

 

 

 

 

 

 

 

 

 

2. 알람 권한 

사용하는 어플들

- 캐시** 사용

- 토* 사용

 

나는 위 권한에 대해서 조금 의외로 생각했다.

하지만 대표적인 만보기 어플이 모두 이 권한에 대해서 강조했다.

내가 이 권한에 대해 의외로 생각한 이유는 위에서 썼듯 알림탭에 있는 알림만 지울 수 있는 것이지 포그라운드 서비스를 종료시키는건 아니라고 생각했기 때문이다. (위 1. 안드로이드 13 (API 레벨 33) 부터 사용자가 포그라운드 서비스와 연결된 알림을 즉시 닫을 수 있다.)

근데 대표적인 만보기 어플 모두 이 권한에 대해서 강조하는 것을 보면 백그라운드 작업에 영향은 있는건가 싶다.

 

 

 

 

 

 

 

 

 

 

 

 

걸음수 자정 초기화는 어떻게 구현될까?

12시 자정 초기화 되면 걸음수를 0으로 초기화 시켜줘야 하는데 이것도 사실 어떤 방법에 따라 배터리 최적화에 영향을 받을 수 있을 것 같아서 이 내용 또한 포함시킨다.

일부 만보기 기능 어플에서는 걸음수 자정 초기화 구현이 안된 곳도 있는 것 같다. (ex. 대표 어플: 토*, 캐시**)

그래도 이것을 구현하는 데에 있어서 여러 방법이 있을 것 같은데 그 중 몇 가지만 소개해보겠다.

 

 

 

 

1. 알람 매니저

알람매니저는 시간 기반의 작업을 스케줄링하는 특히 유용하며, 정확한 시간에 작업을 실행하거나 반복적인 알람을 설정하는 적합한 컴포넌트이다.

아래처럼 Manifest 와 Application 태그를 작성해주면 된다. (Flutter 기준)

 

 

Manifest 

 

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- For apps with targetSDK 31 (Android 12) and newer -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>

 

 

 

Application 

 

<service
    android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="false"/>
<receiver
    android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
    android:exported="false"/>
<receiver
    android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
    android:enabled="false"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

 

 

타사 알람 어플도 알람 매니저를 이용하니 그래도 설정한 시간대 쯔음에 울릴 줄 알았는데 테스트를 몇 번 해 본 결과, 알람이 울릴 때도 있었고 안 울릴 때도 있었다. 😱

이유를 분석해보니 설정에서 절전 모드가 켜 있을 때는 알람 매니저가 동작을 안한 것 같았다.

 

 

대표적인 타사 알람 어플의 문의사항에도 알림이 안 울리는 것에 대한 사유, 원인이 적혀 있었다.

알람 어플이어도 배터리 절전모드, 배터리 사용량 최적화를 피해갈 수 없었다니.. 충격이긴 했다.

 

 

 

 

 

 

2. FCM 우선순위 높은 메시지 사용

결국 정확한 시간에 동작하는 기능을 구현하기 위해서는 서버를 이용한 FCM 을 이용하는 것이었다.

기기에서 자정에 정확하게 동작하는 기능은 OS 에 강력하게 제한이 되버리니 결국 24시간 내내 살아있는 외부 서버 밖에 답이 없었다.

물론 이것 또한 모든 상황(절전모드, 배터리 최적화, 도즈모드) 속에서 FCM 메시지가 기기를 깨울 수 있는 것도 아니었다. 

오직 우선순위가 높은 FCM 메시지만이 기기를 깨울 수 있었다.

현재 위 기능으로 우리는 걸음수 자정 초기화를 시키고 있다.

 

 

 

 

 

 

결론

결국 앱 개발자가 만보기 기능을 구현하려면 안드로이드가 배터리 수명을 개선시키려는 노력에 맞서 싸워야 한다는 결론을 얻었다. 거기서 알람 어플 또한 피해갈 수 없다는 사실에 놀랍기만 하다.

 

구글 플레이 스토어에서 타사 만보기 앱 리뷰들을 살펴보면 거의 모두 배터리 소모량에 대해 불평하는 리뷰들이 많이 보였다.

근데 이걸 개발자의 입장에서 해결할 수도 없다는 것을 알아줬으면 좋겠다. 아니면 안드로이드가 iOS 처럼 하루 걸음수를 제공해주는 API 를 제공해주던가..

 

만보기 기능을 개발하기 위해서 여러모로 불편한 개발 포인트가 많다는 결론을 내리면서 이 글을 마무리 한다.

만보기 개발자들 화이팅..!