앱에서 포그라운드 서비스를 실행하는 단계는 두 가지입니다. 먼저 context.startForegroundService()
를 호출하여 서비스를 시작해야 합니다. 그런 다음 서비스가 ServiceCompat.startForeground()
를 호출하여 포그라운드 서비스로 승격합니다.
기본 요건
앱이 타겟팅하는 API 수준에 따라 앱이 포그라운드 서비스를 실행할 수 있는 시점에 몇 가지 제한사항이 있습니다.
Android 12 (API 수준 31) 이상을 타겟팅하는 앱은 몇 가지 예외를 제외하고 앱이 백그라운드에 있는 동안 포그라운드 서비스를 시작할 수 없습니다. 이 규칙의 예외와 관련된 자세한 내용은 백그라운드에서 포그라운드 서비스를 시작할 때 적용되는 제한사항을 참고하세요.
Android 14 (API 수준 34) 이상을 타겟팅하는 앱은 포그라운드 서비스 유형에 적절한 권한을 요청해야 합니다. 앱이 서비스를 포그라운드로 승격하려고 하면 시스템은 적절한 권한을 확인하고 앱에 권한이 없는 경우
SecurityException
을 발생시킵니다. 예를 들어location
유형의 포그라운드 서비스를 실행하려고 하면 시스템은 앱에 이미ACCESS_COARSE_LOCATION
또는ACCESS_FINE_LOCATION
권한이 있는지 확인합니다. 포그라운드 서비스 유형 문서에는 각 포그라운드 서비스 유형에 필요한 기본 요건이 나열되어 있습니다.
서비스 실행
포그라운드 서비스를 실행하려면 먼저 일반 (포그라운드가 아닌) 서비스로 실행해야 합니다.
Kotlin
val intent = Intent(...) // Build the intent for the service context.startForegroundService(intent)
자바
Context context = getApplicationContext(); Intent intent = new Intent(...); // Build the intent for the service context.startForegroundService(intent);
코드 관련 핵심 사항
- 코드 스니펫이 서비스를 실행합니다. 하지만 서비스는 아직 포그라운드에서 실행되지 않습니다. 서비스 자체 내에서
ServiceCompat.startForeground()
를 호출하여 서비스를 포그라운드 서비스로 승격해야 합니다.
서비스를 포그라운드로 승격
서비스가 실행되면 ServiceCompat.startForeground()
를 호출하여 서비스가 포그라운드에서 실행되도록 요청해야 합니다. 일반적으로 서비스의 onStartCommand()
메서드에서 이 메서드를 호출합니다.
ServiceCompat.startForeground()
는 다음 매개변수를 사용합니다.
- 서비스
- 상태 표시줄에서 서비스의 알림을 고유하게 식별하는 양의 정수입니다.
Notification
객체 자체입니다.- 서비스에서 실행한 작업을 식별하는 포그라운드 서비스 유형
특정 사용 사례에 따라 startForeground()
매니페스트에 선언된 유형에 전달하는 포그라운드 서비스 유형입니다. 그런 다음 서비스 유형을 더 추가해야 하는 경우 startForeground()
를 다시 호출할 수 있습니다.
예를 들어 피트니스 앱이 항상 location
정보가 필요하지만 미디어를 재생해야 할 수도 있고 아닐 수도 있는 달리기 추적 서비스를 실행한다고 가정해 보겠습니다. 매니페스트에서 location
와 mediaPlayback
를 모두 선언해야 합니다. 사용자가 달리기를 시작하고 위치만 추적하려는 경우 앱은 startForeground()
를 호출하고 ACCESS_FINE_LOCATION
권한만 전달해야 합니다. 그런 다음 사용자가 오디오 재생을 시작하려는 경우 startForeground()
를 다시 호출하고 모든 포그라운드 서비스 유형 (이 경우 ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK
)의 비트 조합을 전달합니다.
다음 예는 카메라 서비스가 포그라운드 서비스로 승격하는 데 사용하는 코드를 보여줍니다.
Kotlin
class MyCameraService: Service() { private fun startForeground() { // Before starting the service as foreground check that the app has the // appropriate runtime permissions. In this case, verify that the user has // granted the CAMERA permission. val cameraPermission = PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA) if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) { // Without camera permissions the service cannot run in the foreground // Consider informing user or updating your app UI if visible. stopSelf() return } try { val notification = NotificationCompat.Builder(this, "CHANNEL_ID") // Create the notification to display while the service is running .build() ServiceCompat.startForeground( /* service = */ this, /* id = */ 100, // Cannot be 0 /* notification = */ notification, /* foregroundServiceType = */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA } else { 0 }, ) } catch (e: Exception) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && e is ForegroundServiceStartNotAllowedException) { // App not in a valid state to start foreground service // (e.g. started from bg) } // ... } } }
자바
public class MyCameraService extends Service { private void startForeground() { // Before starting the service as foreground check that the app has the // appropriate runtime permissions. In this case, verify that the user // has granted the CAMERA permission. int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA); if (cameraPermission == PackageManager.PERMISSION_DENIED) { // Without camera permissions the service cannot run in the // foreground. Consider informing user or updating your app UI if // visible. stopSelf(); return; } try { Notification notification = new NotificationCompat.Builder(this, "CHANNEL_ID") // Create the notification to display while the service // is running .build(); int type = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; } ServiceCompat.startForeground( /* service = */ this, /* id = */ 100, // Cannot be 0 /* notification = */ notification, /* foregroundServiceType = */ type ); } catch (Exception e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && e instanceof ForegroundServiceStartNotAllowedException ) { // App not in a valid state to start foreground service // (e.g started from bg) } // ... } } //... }
코드 관련 핵심 사항
- 앱은 이미 매니페스트에서
CAMERA
권한이 필요하다고 선언했습니다. 그러나 앱은 런타임 시에도 사용자가 해당 권한을 부여했는지 확인해야 합니다. 앱에 올바른 권한이 실제로 없는 경우 사용자에게 문제를 알려야 합니다. - Android 플랫폼의 버전에 따라 다양한 포그라운드 서비스 유형이 도입되었습니다. 이 코드는 실행 중인 Android 버전을 확인하고 적절한 권한을 요청합니다.
- 코드는 허용되지 않는 상황에서 포그라운드 서비스를 시작하려고 하는 경우
ForegroundServiceStartNotAllowedException
를 확인합니다 (예: 앱이 백그라운드에 있는 동안 서비스를 포그라운드로 승격하려는 경우).