Los servicios en primer plano realizan operaciones visibles para el usuario.
Los servicios en primer plano muestran una notificación en la barra de estado para que los usuarios sepan que la app está realizando una tarea en primer plano y que está consumiendo recursos del sistema.
Estos son algunos ejemplos de apps que usan servicios en primer plano:
- Esta es una app de reproductor de música que reproduce música en un servicio en primer plano. La notificación podría indicar la canción actual que se está reproduciendo.
- Esta es una app de fitness que registra la actividad de correr de un usuario en un servicio en primer plano después de obtener el permiso del usuario. La notificación puede mostrar la distancia que recorrió el usuario durante la sesión de entrenamiento actual.
Usa un servicio en primer plano solo cuando la app necesite realizar una tarea que el usuario pueda ver, incluso cuando no esté interactuando directamente con ella. Si la acción tiene una importancia lo suficientemente baja como para usar una notificación de prioridad mínima, crea una tarea en segundo plano.
En este documento, se describe el permiso necesario para usar servicios en primer plano y cómo iniciarlos y quitarlos del segundo plano. También se explica cómo asociar ciertos casos de uso con tipos de servicios en primer plano y las restricciones de acceso que se aplican cuando se inicia un servicio en primer plano desde una app que se ejecuta en segundo plano.
El usuario puede descartar la notificación de forma predeterminada
A partir de Android 13 (nivel de API 33), los usuarios pueden descartar la notificación asociada con un servicio en primer plano de forma predeterminada. Para ello, los usuarios realizan un gesto de deslizamiento en la notificación. Tradicionalmente, la notificación no se descarta, a menos que se detenga o se quite el servicio en primer plano.
Si deseas que el usuario no pueda descartar la notificación, pasa true
al método setOngoing()
cuando crees la notificación con Notification.Builder
.
Servicios que muestran una notificación de inmediato
Si un servicio en primer plano tiene al menos una de las siguientes características, el sistema muestra la notificación asociada inmediatamente después de que se inicia el servicio, incluso en dispositivos que ejecutan Android 12 o versiones posteriores:
- El servicio está asociado con una notificación que incluye botones de acción.
- El servicio tiene un
foregroundServiceType
demediaPlayback
,mediaProjection
ophoneCall
. - El servicio proporciona un caso de uso relacionado con las llamadas telefónicas, la navegación o la reproducción multimedia, como se define en el atributo de categoría de la notificación.
- Para inhabilitar el cambio de comportamiento, el servicio pasó
FOREGROUND_SERVICE_IMMEDIATE
asetForegroundServiceBehavior()
cuando configuró la notificación.
En Android 13 (nivel de API 33) o versiones posteriores, si el usuario rechaza el permiso de notificaciones, seguirá viendo avisos relacionados con los servicios en primer plano en el Administrador de tareas, pero no los verá en el panel lateral de notificaciones.
Declara los servicios en primer plano en tu manifiesto
En el manifiesto de la app, declara cada uno de sus servicios en primer plano con un elemento <service>
. Para cada servicio, usa un atributo android:foregroundServiceType
para declarar el tipo de trabajo que realiza el servicio.
Por ejemplo, si tu app crea un servicio en primer plano que reproduce música, puedes declarar el servicio de la siguiente manera:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
<application ...>
<service
android:name=".MyMediaPlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
</service>
</application>
</manifest>
Si tus varios tipos se aplican a tu servicio, sepáralos con el operador |
. Por ejemplo, un servicio que usa la cámara y el micrófono lo declararía de la siguiente manera:
android:foregroundServiceType="camera|microphone"
Solicita los permisos de servicio en primer plano
Las apps que se orientan a Android 9 (nivel de API 28) o versiones posteriores y usan servicios en primer plano deben solicitar el objeto FOREGROUND_SERVICE
en el manifiesto de la app, como se muestra en el siguiente fragmento de código. Este es un permiso normal, por lo que el sistema se lo otorgará automáticamente a la app que lo solicite.
Además, si la app se orienta al nivel de API 34 o superior, debe solicitar el tipo de permiso adecuado para el tipo de trabajo que realizará el servicio en primer plano. Cada tipo de servicio en primer plano tiene un tipo de permiso correspondiente. Por ejemplo, si una app inicia un servicio en primer plano que usa la cámara, debes solicitar los permisos FOREGROUND_SERVICE
y FOREGROUND_SERVICE_CAMERA
. Todos estos son permisos normales, por lo que el sistema los otorga automáticamente si aparecen en el manifiesto.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>
<application ...>
...
</application>
</manifest>
Requisitos previos del servicio en primer plano
A partir de Android 14 (nivel de API 34), cuando inicias un servicio en primer plano, el sistema verifica los requisitos previos específicos según el tipo de servicio. Por ejemplo, si intentas iniciar un servicio en primer plano de tipo location
, el sistema verifica que tu app ya tenga el permiso ACCESS_COARSE_LOCATION
o ACCESS_FINE_LOCATION
. De lo contrario, el sistema arrojará una SecurityException
.
Por este motivo, debes confirmar que se cumplan los requisitos previos necesarios antes de iniciar un servicio en primer plano. En la documentación sobre el tipo de servicio en primer plano, se enumeran los requisitos previos para cada tipo de servicio en primer plano.
Cómo iniciar un servicio en primer plano
Antes de solicitarle al sistema que ejecute un servicio como servicio en primer plano, inicia el servicio en sí:
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);
Dentro del servicio, por lo general, en onStartCommand()
, puedes solicitar que se ejecute en primer plano. Para ello, llama a ServiceCompat.startForeground()
(disponible en androidx-core 1.12 y versiones posteriores). Este método toma los siguientes parámetros:
- El servicio
- Un número entero positivo que identifica de forma única la notificación en la barra de estado
- El objeto
Notification
- Los tipos de servicio en primer plano que identifican el trabajo que realiza el servicio
Estos tipos pueden ser un subconjunto de los 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 actividades de correr que siempre necesita información de location
, pero que puede o no necesitar reproducir contenido multimedia. Tendrías que declarar location
y mediaPlayback
en el manifiesto. Si un usuario inicia una actividad de correr 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 a nivel de bits de todos los tipos de servicios en primer plano (en este caso, ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK
).
Este es un ejemplo que inicia un servicio en primer plano de la cámara:
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) } // ... } } //... }
Quita un servicio del primer plano
Para quitar el servicio del primer plano, llama a stopForeground()
.
Este método tiene un valor booleano, que indica si también se debe quitar la notificación de la barra de estado. Ten en cuenta que el servicio seguirá ejecutándose.
Si detienes el servicio mientras se ejecuta en primer plano, se quitará su notificación.
Cómo controlar la detención iniciada por el usuario de las apps que ejecutan servicios en primer plano
A partir de Android 13 (nivel de API 33), los usuarios pueden completar un flujo de trabajo desde el panel lateral de notificaciones para detener una app que tenga servicios en primer plano en curso, independientemente de la versión del SDK de destino de la app. Esta opción, llamada Administrador de tareas, muestra una lista de apps que actualmente ejecutan un servicio en primer plano.
Esta lista tiene la etiqueta Apps activas. Junto a cada app, hay un botón Detener. En la Figura 1, se ilustra el flujo de trabajo del Administrador de tareas en un dispositivo que ejecuta Android 13.
Cuando el usuario presiona el botón Detener junto a tu app en el Administrador de tareas, se producen las siguientes acciones:
- El sistema quita la app de la memoria. Por lo tanto, se detiene toda la app, no solo el servicio en primer plano en ejecución.
- El sistema quita la pila de actividades de actividad de tu app.
- Se detendrá cualquier reproducción de contenido multimedia.
- Se quita la notificación asociada con el servicio en primer plano.
- Tu app permanecerá en el historial.
- Los trabajos programados se ejecutan a la hora programada.
- Las alarmas suenan a la hora o en el período programado.
Para probar que tu app se comporte como se espera mientras un usuario la detiene y una vez que la detiene, ejecuta el siguiente comando de ADB en una ventana de terminal:
adb shell cmd activity stop-app PACKAGE_NAME
Exenciones
El sistema brinda varios niveles de exenciones para ciertos tipos de apps, que se describen en las siguientes secciones.
Las exenciones se aplican por app y no por proceso. Si el sistema exime un proceso en una app, todos los demás procesos de esa app también quedan eximidos.
Exenciones con respecto a aparecer en el Administrador de tareas
Las siguientes apps pueden ejecutar un servicio en primer plano y no aparecer en el Administrador de tareas:
- Apps a nivel del sistema
- Apps de seguridad (es decir, aquellas que tienen el rol
ROLE_EMERGENCY
) - Dispositivos que están en el modo de demostración
Exenciones con respecto a la finalización por parte de los usuarios
Cuando los siguientes tipos de apps ejecutan un servicio en primer plano, aparecen en el Administrador de tareas, pero no hay un botón Detener junto al nombre de la app para que el usuario lo presione:
- Apps del propietario del dispositivo
- Apps del propietario del perfil
- Apps persistentes
- Apps que tienen el rol
ROLE_DIALER
Usa APIs específicas en lugar de servicios en primer plano
En muchos casos de uso, existen APIs de la plataforma o de Jetpack que puedes usar para realizar tareas para las que, de otro modo, usarías un servicio en primer plano. Si existe una API diseñada para propósitos específicos, debes usarla casi siempre en lugar de usar un servicio en primer plano. Las APIs con propósitos específicos suelen proporcionar capacidades adicionales específicas de casos de uso que, de lo contrario, tendrías que compilar por tu cuenta. Por ejemplo, la API de Bubbles controla la lógica compleja de la IU para las apps de mensajería que necesitan implementar funciones de burbujas de chat.
En la documentación de los tipos de servicios en primer plano, se enumeran buenas alternativas para usar en lugar de los servicios en primer plano.
Restricciones para iniciar un servicio en primer plano desde el segundo plano
Las apps orientadas a Android 12 o versiones posteriores no pueden iniciar servicios en primer plano mientras se ejecuta en segundo plano, excepto en algunos casos especiales. Si una app intenta iniciar un servicio en primer plano mientras se ejecuta en segundo plano y el servicio en primer plano no cumple con uno de los casos excepcionales, el sistema arroja una ForegroundServiceStartNotAllowedException
.
Además, si una app quiere iniciar un servicio en primer plano que necesita permisos durante el uso (por ejemplo, permisos de sensor corporal, cámara, micrófono o ubicación), no puede crear el servicio mientras la app está en segundo plano, incluso si la app se encuentra en una de las exenciones de las restricciones de inicio en segundo plano. El motivo se explica en la sección Restringir el inicio de servicios en primer plano que necesitan permisos durante el uso.
Exenciones de las restricciones para el inicio en segundo plano
En las siguientes situaciones, tu app puede iniciar servicios en primer plano incluso mientras se ejecuta en segundo plano:
- La app viene de un estado visible para el usuario, como una actividad.
- La app puede iniciar una actividad en segundo plano, excepto en los casos en que tiene una actividad en la pila de actividades de una tarea existente.
Tu app recibe un mensaje de prioridad alta mediante Firebase Cloud Messaging.
El usuario realiza una acción en un elemento de la IU relacionado con tu app. Por ejemplo, puede interactuar con un cuadro de ayuda, una notificación, un widget o una actividad.
Tu app invoca una alarma exacta para completar una acción que solicita el usuario.
Tu app es el método de entrada actual del dispositivo.
Tu app recibe un evento relacionado con el geovallado o la transición del reconocimiento de actividad.
Después de que el dispositivo se reinicia y recibe la acción de intent
ACTION_BOOT_COMPLETED
,ACTION_LOCKED_BOOT_COMPLETED
oACTION_MY_PACKAGE_REPLACED
en un receptor de emisión.La app recibe la acción de intent
ACTION_TIMEZONE_CHANGED
,ACTION_TIME_CHANGED
oACTION_LOCALE_CHANGED
en un receptor de emisión.Tu app recibe el evento
ACTION_TRANSACTION_DETECTED
deNfcService
.Apps que tienen permisos o funciones del sistema específicos, como propietarios del dispositivo y propietarios de perfiles.
Tu app usa el administrador de dispositivos complementarios y declara el permiso
REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND
oREQUEST_COMPANION_RUN_IN_BACKGROUND
. Siempre que sea posible, usaREQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND
.El usuario desactiva las optimizaciones de la batería para tu app.
Tu app tiene el permiso
SYSTEM_ALERT_WINDOW
. Nota: Si tu app se orienta a Android 15 o versiones posteriores, debe tener el permisoSYSTEM_ALERT_WINDOW
y, actualmente, debe tener una ventana superpuesta visible.
Restricciones sobre el inicio de servicios en primer plano que necesitan permisos durante el uso
En Android 14 (nivel de API 34) o versiones posteriores, hay situaciones especiales que debes tener en cuenta si inicias un servicio en primer plano que necesita permisos durante el uso.
Si tu app se segmenta para Android 14 o versiones posteriores, el sistema operativo verifica cuando creas un servicio en primer plano para asegurarse de que tu app tenga todos los permisos adecuados para ese tipo de servicio. Por ejemplo, cuando creas un servicio en primer plano del tipo microphone, el sistema operativo verifica que tu app tenga actualmente el permiso RECORD_AUDIO
. Si no tienes ese permiso, el sistema arroja una SecurityException
.
En el caso de los permisos durante el uso, esto puede causar un problema potencial. Si tu app tiene un permiso durante el uso, solo lo tendrá mientras esté en primer plano. Esto significa que, si tu app está en segundo plano y trata de crear un servicio en primer plano de tipo cámara, ubicación o micrófono, el sistema ve que tu app actualmente no tiene los permisos necesarios y arroja una SecurityException
.
Del mismo modo, si tu app está en segundo plano y crea un servicio de salud que necesita el permiso BODY_SENSORS
, la app no tiene ese permiso en ese momento y el sistema arroja una excepción.
(Esto no se aplica si se trata de un servicio de salud que necesita permisos diferentes, como ACTIVITY_RECOGNITION
). Llamar a PermissionChecker.checkSelfPermission()
no evita este problema. Si tu app tiene un permiso durante el uso y llama a checkSelfPermission()
para verificar si tiene ese permiso, el método muestra PERMISSION_GRANTED
, incluso si la app está en segundo plano. Cuando el método muestra PERMISSION_GRANTED
, indica "tu app tiene este permiso mientras la app está en uso".
Por este motivo, si tu servicio en primer plano necesita un permiso durante el uso, debes llamar a Context.startForegroundService()
o Context.bindService()
mientras tu app tiene una actividad visible, a menos que el servicio se encuentre en una de las exenciones definidas.
Exenciones de las restricciones sobre los permisos durante el uso
En algunas situaciones, incluso si se inicia un servicio en primer plano mientras la app se ejecuta en segundo plano, puede acceder a la información de la ubicación, la cámara y el micrófono mientras la app se ejecuta en primer plano ("durante el uso").
En estas mismas situaciones, si el servicio declara un tipo de servicio en primer plano de location
y es iniciado por una app que tiene el permiso ACCESS_BACKGROUND_LOCATION
, este servicio puede acceder a la información de la ubicación en todo momento, incluso cuando la app se ejecuta en segundo plano.
La siguiente lista contiene estas situaciones:
- Un componente del sistema inicia el servicio.
- El servicio se inicia interactuando con los widgets de la app.
- El servicio comienza interactuando con una notificación.
- El servicio se inicia como una
PendingIntent
que se envía desde una app diferente y visible. - Una app que es un controlador de política de dispositivo y se ejecuta en el modo de propietario del dispositivo inicia el servicio.
- Una app que proporciona el
VoiceInteractionService
inicia el servicio. - Una app que tiene el permiso con privilegios
START_ACTIVITIES_FROM_BACKGROUND
inicia el servicio.
Cómo determinar qué servicios se ven afectados en tu app
Cuando pruebes tu app, inicia sus servicios en primer plano. Si un servicio iniciado tiene acceso restringido a la ubicación, el micrófono y la cámara, aparecerá el siguiente mensaje en Logcat:
Foreground service started from background can not have \ location/camera/microphone access: service SERVICE_NAME