앱에서 포그라운드 서비스를 실행하는 데는 두 단계가 있습니다. 먼저 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
를 확인합니다.