Hay dos pasos para iniciar un servicio en primer plano desde tu app. Primero, debes iniciar el servicio llamando a context.startForegroundService()
. Luego, haz que el servicio llame a ServiceCompat.startForeground()
para promocionarse como un servicio en primer plano.
Requisitos previos
Según el nivel de API al que se orienta tu app, existen algunas restricciones sobre cuándo una app puede iniciar un servicio en primer plano.
Las apps que se segmentan para Android 12 (nivel de API 31) o versiones posteriores no pueden iniciar un servicio en primer plano mientras la app se ejecuta en segundo plano, con algunas excepciones específicas. Para obtener más información y conocer las excepciones a esta regla, consulta Restricciones para iniciar un servicio en primer plano desde el segundo plano.
Las apps que segmentan Android 14 (nivel de API 34) o versiones posteriores deben solicitar los permisos adecuados para el tipo de servicio en primer plano. Cuando la app intenta promover un servicio a primer plano, el sistema verifica los permisos adecuados y arroja
SecurityException
si la app no tiene alguno. Por ejemplo, si intentas iniciar un servicio en primer plano de tipolocation
, el sistema verifica que tu app ya tenga el permisoACCESS_COARSE_LOCATION
oACCESS_FINE_LOCATION
. En la documentación sobre el tipo de servicio en primer plano, se enumeran los requisitos previos necesarios para cada tipo de servicio en primer plano.
Cómo iniciar un servicio
Para iniciar un servicio en primer plano, primero debes iniciarlo como un servicio ordinario (no en primer plano):
Kotlin
val intent = Intent(...) // Build the intent for the service context.startForegroundService(intent)
Java
Context context = getApplicationContext(); Intent intent = new Intent(...); // Build the intent for the service context.startForegroundService(intent);
Puntos clave sobre el código
- El fragmento de código inicia un servicio. Sin embargo, el servicio aún no se está ejecutando en primer plano. Dentro del servicio, debes llamar a
ServiceCompat.startForeground()
para promover el servicio a un servicio en primer plano.
Promociona un servicio al primer plano
Una vez que se ejecuta un servicio, debes llamar a ServiceCompat.startForeground()
para solicitar que el servicio se ejecute en primer plano. Por lo general, llamarías a este método en el método onStartCommand()
del servicio.
ServiceCompat.startForeground()
toma los siguientes parámetros:
- Es el servicio.
- Es un número entero positivo que identifica de forma única la notificación del servicio en la barra de estado.
- El objeto
Notification
en sí. - El tipo o los tipos de servicio en primer plano que identifican el trabajo realizado por el servicio
Los tipos de servicio en primer plano que pasas a startForeground()
tipos declarados en el manifiesto, según el caso de uso específico. Luego, si necesitas agregar más tipos de servicios, puedes volver a llamar a startForeground()
.
Por ejemplo, supongamos que una app de fitness ejecuta un servicio de seguimiento de carreras que siempre necesita información de location
, pero que podría o no necesitar reproducir contenido multimedia. Deberás declarar location
y mediaPlayback
en el manifiesto. Si un usuario comienza una carrera y solo quiere que se haga un seguimiento de su ubicación, tu app debe llamar a startForeground()
y pasar solo el permiso ACCESS_FINE_LOCATION
. Luego, si el usuario quiere comenzar a reproducir audio, vuelve a llamar a startForeground()
y pasa la combinación bit a bit de todos los tipos de servicios en primer plano (en este caso, ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK
).
En el siguiente ejemplo, se muestra el código que usaría un servicio de cámara para promocionarse como servicio en primer plano:
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) } // ... } } }
Java
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) } // ... } } //... }
Puntos clave sobre el código
- La app ya declaró en el manifiesto que necesita el permiso de
CAMERA
. Sin embargo, la app también debe verificar durante el tiempo de ejecución para asegurarse de que el usuario otorgó ese permiso. Si la app no tiene los permisos correctos, debe informarle al usuario sobre el problema. - Se introdujeron diferentes tipos de servicios en primer plano con distintas versiones de la plataforma de Android. Este código verifica en qué versión de Android se ejecuta y solicita los permisos correspondientes.
- El código verifica
ForegroundServiceStartNotAllowedException
en caso de que intente iniciar un servicio en primer plano en una situación no permitida (por ejemplo, si intenta promover el servicio a primer plano mientras la app está en segundo plano).