본문 바로가기

Android/Troubleshooting

ForegroundService 문제 해결하기: ForegroundServiceDidNotStartInTimeException

월요일 좋아

근데 출근하고 늘 체크하는 Firebase Crashlystics에서 늘 고질적으로 발생하는 오류의 빈도가 향상된 것을 발견했다.

월요일 안좋아

 

개인적으로 수정해서 빨리 눈앞에서 치워버리고 싶었는데, 지금까진 다른 일이 바빠서 해결을 어영부영 미루던 이슈였다.

마침 진행 중이던 바쁜 일도 끝났고, 오늘도 어김없이 문제 목록의 상단에서 날 반겨주었기에 그냥 처리해버리기로 마음 먹었다.

 

내용 추가: 24.01.24


android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException

 

이 오류는 서비스가 시작될 startForegroundService() 메소드를 호출한 일정 시간 내에 startForeground() 메소드를 호출하지 않았을 발생한다.

환경마다 차이가 있겠지만, Android 8.0 (Api level 26, Oreo)이후로 평균 5초 이내에 서비스가 실행되지 않으면 발생하는 오류다.

 

현재 담당 중인 앱은 배송과 관련된 앱으로, 위치 정보가 매우 중요하다.

그래서 매 분마다 새로운 위치 정보를 서비스를 통해 갱신한다.

만약 서비스가 3분 이상 이를 갱신하지 않았다면 앱이 전면부에 돌아왔을때 서비스를 재시작 하도록 하는데..

여기서 앱 터진다.

 

기존 서비스는 onCreate()에서 startForeground()로 서비스를 실행하고, onDestroy()에서 stopService()를 호출해 서비스를 제거하는 방식이었다.

override fun onCreate() {
    super.onCreate()
    startForeground(MyApp, createChannel().build())
}
    
override fun onDestory() {
    super.onDestory()
    stopService(Intent(applicationContext, MyService::class.java))
}

 

갱신 시에는 stopService()와 startForegroundService()를 액티비티에서 직접 호출하는 방식이다.

// 액티비티에 작성되어 있던 서비스 재시작 코드

if (interval > 3) {
        stopService(Intent(applicationContext, MyService::class.java))

        ContextCompat.startForegroundService(
            this@TempActivity::class.java,
            Intent(applicationContext, MyService::class.java)
        )
    }
}

 

이 코드에서는 서비스를 중지시킨 후, 새로운 서비스를 포그라운드 모드로 시작한다.

하지만 여기서 서비스가 반드시 5초 이내에 실행된다는 보장은 없다.

 

stopService를 한다고해서 즉각적으로 서비스의 onDestroy가 호출되는 것은 아니기 때문이다.

시스템 리소스의 가용성이나 다른 시스템 조건에 따라 stopService이후 onDestory가 실행되기 까지 약간의 시간이 필요할 수 있기 때문이다.

 

아무튼 일단 이렇게 특정 액티비티에서 startForegroundService()를 호출하고 서비스에서는 startForeground()로 이어지는 코드를 수정하면 해결되지 않을까?? 라는 생각에, 서비스부터 갱신 코드까지 구글이 권장하는 방식을 기반으로 수정했다.

 

기존에는 사용하지 않던 onBind와 onStartCommand를 사용하여 서비스를 직접 실행시키는 것이 아닌 bind / unbind 시키는 방식으로 수정했다.

 

해당 내용은 샘플 코드로 따로 게시하겠다.

현재 작성 중인 내용이라 곧 추가 될 예정입니다.

 

Ref.

https://developer.android.com/develop/background-work/services#Lifecycle


내용 추가: 2024. 01. 24

어림도 없지ㅋㅋ 여전히 발생한다.

앞서 시도한 방법을 적용함에도 ForegroundServiceStartNotAllowedException이 문제가 빈도만 줄었지(한 5% 줄었나..) 사라지질 않았다.

아니 비슷한 Foreground Service 오류지만, 내용이 바뀌었다.

android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service giosis.com.communication.qxquick_sg/.service.LocationService

 

mAllowStartForeground 플래그가 false라서 안된다는 내용인데, 열심히 고민해봐도 답이 안나와 구글링을 해봤다. 많은 사람들이 동일한 고민을 하고 있는 것에 조금 위안을 받았다.

 

그럼 이게 뭔데 이럴까?

  • mAllowStartForeground는 안드로이드 OS 내부 구현에 있는 변수로, 시스템 레벨에서 앱이 Foreground Service를 시작할 수 있는지 여부를 결정하는 데 사용된다.
  • 이게 ture면 앱은 포그라운드 서비스를 실행할 수 있고, false면 시작할 수 없다.

 

Android 12(Api level 31)부터 앱이 백그라운드에 있을 때 포그라운드 서비스를 시작하는 것에 대한 제한을 강화했다.

이게 문제다. 백그라운드에서 포그라운드 서비스를 시작하려는 앱의 요청을 시스템이 거부할 수 있어졌다는 것이기 때문이다.

대게는 배터리 수명과 관련된 문제인데..

 

아무튼 앱이 아래 내용 중 하나라도 해당되지 않는다면 mAllowStartForeground는 false일 것이고, 앱은 ForegroundServiceStartNotAllowedException를 일으킬 수 있다.

  • 앱이 액티비티처럼 사용자에게 보이는 상태에서 전환된 경우.
  • 앱은 백그라운드에서 액티비티를 시작할 수 있는 경우. 단, 앱이 기존 작업의 백 스택에 활동을 보유한 경우는 예외(무슨 소린지 잘 몰름보)
  • 앱이 Firebase Cloud Messaging을 통해 우선 순위가 높은 메시지를 수신하는 경우
  • 사용자가 앱과 관련된 UI 요소에서 작업을 수행하는 경우(예를 들어 버블, 알림, 위젯 또는 액티티비 등과 상호 작용하는 것)
  • 사용자 요청에 따른 작업을 완료하기 위해 정확한 알람을 호출하는 경우(이것도 잘 몰르겠다)
  • 앱이 디바이스의 현재 입력 방식으로 사용되는 경우(키보드 같은).
  • 앱이 지오펜싱 또는 활동 인식 전환과 관련된 이벤트를 수신하는 경우.
  • 디바이스가 재부팅되고 브로드캐스트 리시버에서 ACTION_BOOT_COMPLETED, ACTION_LOCKED_BOOT_COMPLETED 또는 ACTION_MY_PACKAGE_REPLACED같은 특정 인텐트 액션을 수신한 경우.

더 자세한 내용은 여기(Developers)를 참고 바란다.

일단 담당 중인 앱은 다 해당이 안된다(ㅋㅋ)

 

 암튼 배터리 수명 관련 문제이면, 배터리 최적화를 끄면 안될까?

그럼 Doze모드에서 안전해져서 문제가 해결되지 않을까??

앱 세팅의 배터리 최적화

 

그래서 Splash Activity에서 배터리 최적화 활성 여부를 점검해 비활성화 위한 다이얼로그를 띄워주기로 하였다.

 

// 1. 배터리 최적화 허용 여부 확인, falsea면 최적화 기능 사용 중.
val isIgnoringBatteryOptimizations =
            (getSystemService(Context.POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(
                packageName
            )

// 2. 최적화 기능을 사용중이라면 해제를 요청
if (!isIgnoringBatteryOptimizations) {
    val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
    intent.data = Uri.parse("package:$packageName")
    activityResultLauncher.launch(intent)
}

 

이걸 실행하면 아래와 같은 다이얼로그가 표시된다.

 

이번 업데이트에 적용시켜 보고 잘 되는지 후기를 반드시 남기겠다..

 

### 내용 추가 ###

업데이트: 24.04.09

 

결론: 배터리 최적화 활성 여부를 점검해 비활성화를 적용해도 별반 차이는 없었다.

 

그래서 서비스가 시작되는 로직 자체를 수정했고, 이를 통해 앞선 두 문제를 완전히 해결했다.

https://parade621.tistory.com/83

 

Foreground Service 구현: 바인딩된 서비스

Foreground Service의 개념은 개념편을 참고바람 이번 글에서 다루는 예제는 포그라운드 서비스 동작의 이해를 위해 Hilt, Coroutine, Flow 와 같은 고급 기능 사용을 지양하여 작성되었다. 목표는 "위치를

parade621.tistory.com

관련 내용 작성해 뒀으니 위 링크를 통해 확인 바-람


 

### 내용 추가2 ###

업데이트: 24.04.29

 

한동안 전혀 문제가 없다가 오늘 새로운 버전을 업데이트 한 후, 한건 문제가 발생했다.

여전히 "Service.startForeground() not allowed due to mAllowStartForeground false" 요 베라머글 오류가 한건 발생한다. (그래도 평균 월 500회 이상 발생하던게 1건으로 줄었다 ㅋㅋ!)

 

저번 업데이트 때, 도데체 왜 이런 오류가 발생하는지 파악해보려고 firebase에 커스텀 키로 앱의 포그라운드 / 백그라운드 여부를 점검 가능하도록 했는데.. 역시 백그라운드 100%가 뜬다.

 

역시 문제는 앱이 백그라운드에 있을 때, 포그라운드 서비스를 실행하려고해서 발생하는 문제가 맞았다.

그래서 앱이 현재 포그라운드인지, 백그라운드인지 판단하는 함수를 작성 한 후, 전면에 위치한 경우에만 서비스 재시작 로직이 수행되도록 수정했다.

 

해당 함수는 아래처럼 작성해서 사용 중

fun isAppInForeground(context: Context): Boolean {
    val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val appProcess = activityManager.runningAppProcesses ?: return false

    for (info in appProcess) {
        if (info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && info.processName == context.packageName) {
            return true
        }
    }
    return false
}

 

다음번 업데이트에서는 이제 진짜 100% 해결 오류가 되길 바람..


Ref.

https://stackoverflow.com/questions/40434858/action-request-ignore-battery-optimizations-in-not-firing

https://stackoverflow.com/questions/69604951/getting-android-app-foregroundservicestartnotallowedexception-in-android-12-sdk/70378618#70378618

https://stackoverflow.com/questions/70711950/android-12-foregroundservicestartnotallowedexception-while-in-foreground

https://developer.android.com/develop/background-work/services/foreground-services?hl=ko#background-start-restriction-exemptions