startForegroundService / startForeground / startService / stopSelf / stopService 모두 실험 정리
1.
안드로이드 컴포넌트 중 하나인 서비스는 싱글톤이다.
startService()를 여러번 호출한다 한들, 서비스 인스턴스가 여러개 생성되어 진행되진 않는다는 것이다.
onStartCommand() 함수가 여러번 호출되는 것 뿐이다.
그리고, stopSelf()나 stopService()를 호출하면 즉시 서비스가 종료되는 것이 아니라, 시스템에게 '종료를 요청' 하게 되고
시스템은 최대한 빠른 시일 내에 이 요청에 대해 응해주는 방식이다.
이러한 특징으로 인해 고려해야 할 상황이 있다.
서비스는 싱글톤이라 했던 것 처럼 stopSelf() 나 stopService()를 실행하게 되면, 실행중인 해당 서비스 프로세스가 일체 사라진다.
// 알람 취소용
Intent(context, TimerService::class.java).apply {
putExtra("cancel", true)
startService(this)
}
// 알람 시작용
Intent(context, TimerService::class.java).apply {
startService(this)
}
putExtra로 cancel을 넣어준다면, TimerService에서 서비스를 stopSelf()한다고 보자.
이렇게 두 번 연속으로 실행하게 될 경우, 하나의 서비스가 제대로 동작하지 않을 경우가 생길 수도 있다는 점을 고려해야 한다.
첫번째 startService로 stopSelf()를 요청한 상황에서, 아래 startService로 서비스를 재시작하게 될 경우
이게 밀려서, 아예 두번째 서비스가 제대로 실행되지 않고 stopSelf()의 대상이 될 수 있다.
이걸 어떻게 해결하냐, 바로 onStartCommand의 인자로 들어오는 startId를 이용하면 된다.
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {}
stopSelf를 사용할 때 이 인자로 받은 startId를 이용하면, 실행을 요청한 경우를 구분할 수 있다.
실행을 요청한 서비스의 요청코드를 구분하여 종료할 수 있게 되는 것이다.
stopSelf(startId)
2.
startService로 TimerService를 시작하고
TimerService에서 startForeground를 했었는데,
broadcastRecevier에서 startService로 TimerService를 시작하니 오류가 발생한다.
백그라운드에서는 서비스를 시작할 수 없기에 startForegroundService로 시작해야 한다고 한다.
내 프로그램에서
포그라운드에서는
startService -> startForeground
로 하고 있었고,
백그라운드에서는 위처럼 안되어서
startForegroundService -> startForeground
이렇게 했었다.
앱이 포그라운드 상황에선
- startForegroundService() -> startForeground()
- startService() -> startForeground()
두 방식 중 어떤 순서를 이용해도 상관없다.
백그라운드 상황에선 API 26부터, '백그라운드 서비스 제한' 으로 인해
백그라운드에선 백그라운드 서비스를 생성할 수 없으므로,
startForegroundService 로 '포그라운드로 승격될 것을 사전에 알리는 역할'을 진행한 뒤에, startForeground()를 할 수 밖에 없다.
즉 startService로 서비스 생성이 아예 불가능하다.
startForegroundService이 startService와 비교했을 때,
서비스 생성이라는 역할은 같지만, '5초 안에 startForeground()가 호출되어 곧 포그라운드 서비스로 승격될 것.' 임을 알리는 역할을 해준다고 보면 된다.
api 26부터, 백그라운드에서 서비스 실행 제한이 있어서, 백그라운드에선 startService 실행이 안되고 startForegroundService를 통해 "이 서비스가 곧 포그라운드로 승격될 것"을 알려야한다. 그리고 5초안에 startForeground를 호출해야하는것.
3.
startForeground()로 실행된 포그라운드 서비스는 보통 이렇게 두 함수를 연속으로 이용해서 중지하는게 일반적이지만
stopForeground(STOP_FOREGROUND_REMOVE) // 포그라운드에서 백그라운드 서비스로 강등 & Notification 제거
stopSelf() // 서비스 종료
사실 stopForeground() 사용 없이도 stopSelf() 나 stopService()만 이용해도 실행중인 서비스를 종료할 수 있다.
다시말해 서비스 종료에는 문제가 없다.
어떻게 보면 의도에 따라 좀 달라질 수 있다고 보면 되겠다. 필요에 맞게 사용하면 된다.
stopForeground와 stopSelf / stopSerivce의 역할이나 기능이 다르기 때문이다.
이를테면 stopSelf나 stopService만을 사용했다면 notification은 지워지지 않을 테니, NotificationManager 같은 걸로 따로 없애줘야 할 것이다.
위에서 활용성을 말한 stopSelf도 잘 이용해야하는데,
stopSelf(startId)를 이용하게 된다면
작업의 시작을 요청한 서비스 단위와 작업의 종료를 요청한 서비스 단위의 startId가 다르므로 제대로 서비스가 종료되지 않을 수 있다.
이럴 땐 stopSelf에 인자를 넣어주면 안된다.
4.
현재 startForeground() 로 실행중인 포그라운드 서비스가 없다면,
startForegroundService() -> stopForeground(), stopSelf() 만 실행하는 것은 Exception을 발생시킨다.
이게 무슨 말이냐면,
기본적으로 startForegroundService() 호출 뒤에는 5초 안에 startForeground()를 호출해야 하는 것이 국룰이고
호출하지 않을 시 Exception을 뿜는다는 것은 익히 알고 있다.
하지만 이미 startForeground()로 인해 실행중인 포그라운드 서비스가 있을 경우엔,
startForegroundService() 호출 이후 stopForeground()나 stopSelf()만 호출해도 상관없다. startForeground()를 호출 안해도 괜찮다는 것. 이미 포그라운드로 서비스가 동작중이기 때문에 ㅇㅇ..
하지만 현재 실행중인 포그라운드 서비스가 없을 때
startForegroundService() 호출 이후 stopForeground()나 stopSelf()만 호출하는 등 startForeground()를 호출하지 않는다면, 이는Exception을 발생시킨다.
프로그램에서 이런 예외가 발생할 경우가 있다면 주의해야한다.
정리
startForegroundService는, 현재 startForeground() 되어있는게 없으면 startForeground()를 호출하지 않으면 에러. (포그라운드 서비스가 동작중이지 않기 때문)
방안 1.
Service 에서 stopForeground앞에 항상 startForeground를 하나 억지로 둔다.
그렇게 함으로써, 종료 목적이더라도 start를 하고 종료함으로써 예외케이스를 막을 수 있다.
방안 2.
포그라운드 서비스가 동작중이지 않은 상황에서 startForegroundService -> stopForeground, stopSelf 만 하는 것은 오류이므로
startForegroundService 대신 startService를 호출한다. (하지만 이는 포그라운드 상 호출에만 가능. 백그라운드에선 startService가 애초에 불가능)
참고
https://myseong.tistory.com/27
https://developer.android.com/guide/components/services?hl=ko#StartingAService
'[개발 일지]' 카테고리의 다른 글
당신이 잠든 사이에(앱 자동종료 타이머) - Sleep timer 앱 리뉴얼 개발일지 (1) | 2023.06.04 |
---|---|
당잠사 실험..! (죽어나갔었다) (0) | 2023.06.01 |
취소와 확인 버튼의 위치와 순서의 의미는? (0) | 2023.05.25 |
더 이상 기술 개발에만 국한되어서는 안된다 (0) | 2023.05.23 |
PlayStore ASO 적용 기대 (0) | 2023.05.23 |