Definición
Un servicio en Android se asemeja a un demonio en sistemas GNU/Linux, es decir que es un programa en ejecución en segundo plano sin necesidad de interactuar con el usuario. Por lo tanto un servicio no tiene interfaz gráfica.
El termino 'segundo plano' en el contexto de servicios en android puede crear confusión ya que existen los servicios en segundo plano (background) y los servicios en primer plano (foreground).
La creación y ejecución de un servicio en Android no es como en un sistema GNU/Linux (que creas un fichero con la configuración para systemd
y utilizando systemctl
lo pones en ejecución) en este caso el servicio siempre lo deberá ejecutar la aplicación principal.
En Android existen dos tipos de servicios, los Background Services y los Foreground Services. Los dos son muy parecidos pero tiene diferencias notables.
Diferencias
Background Services | Foreground Services |
---|---|
No es necesario notificar al usuario de su ejecución | Es obligatorio mostrar una notificación al usuario (parte superior) |
Tienen baja prioridad para el SO | Alta prioridad para el SO |
El SO lo puede parar | El SO no lo suele parar |
Consume menos recursos | Consume más recursos |
Tiempo de vida corto | Tiempo de vida largo |
No afectan la experiencia del usuario | Afectan la experiencia del usuario |
Para que se usan
Un servicio se puede utilizar para cosas que no tenga que intervenir el usuario y que sea transparente para el. También se puede utilizar para ejecutar funcionalidades en segundo plano con una interacción con el usuario.
Background
Son útiles para tareas que no requieren interacción con el usuario y pueden ejecutarse sin su intervención.
Como casos de uso pueden ser la sincronización de datos, copias de seguridad automáticas y actualización de bases de datos.
Evitar realizar tareas de larga duración ya que el sistema lo puede parar cuando lo vea necesario
Foreground
Al tener que mostrar una notificación permanente cuando se encuentra en ejecución un servicio como este, algunos ejemplos de uso pueden ser: la reproducción de música, al usuario se le informa mediante una notificación de que se reproduce música, permitiendo interactuar con el reproductor parando, pausando, avanzando, retrocediendo.
También se puede utilizar para descargas de ficheros de gran tamaño, donde se muestra al usuario el progreso de la descarga y también se permite la pausa o la cancelación de la misma. (Por ejemplo cuando una aplicación se actualiza, se muestra una notificación de este estilo).
Otro uso que se le puede dar es el seguimiento del usuario mediante GPS e informándole que se están capturando los datos.
En una ocasión he utilizado este tipo de servicio para grabar Audio y Video en segundo plano.
El Audio lo grababa mediante
MediaRecorder
y video medianteCameraX
, la implementación que realicé fue unForeground Service
ya queMediaRecorder
a partir del API 28 no permite el acceso al micrófono desde unBackground Service
.Cuando ocurría determinado evento, otro servicio ejecutaba este servicio (el de grabación) y comenzaba a grabar. Era obligatorio mostrar una notificación al usuario de que estaba siendo grabado.
Implementación
Background Services
Ejemplo de como crear un Background Service que se ejecuta desde un Composable
Servicio
Heredar de Service
y sobrescribir los métodos onBind
, onStartCommand
y onDestroy
.
En el método onStartCommand
si queremos que el servicio se ejecute si se cierra la aplicación retornar la constante START_STICKY
.
class MyBackgroundService : Service() {
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Tarea que va a realizar el servicio
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
}
El Composable
El composable es un botón que recibe dos funciones por parámetro, para iniciar el servicio y para parar el servicio.
Estas funciones en este caso están creadas en la clase main
, lo ideal es tenerlas creadas en un ViewModel
@Composable
fun StartStopServiceButton(
onStartService: () -> Unit,
onStopService: () -> Unit
) {
var isServiceRunning by remember { mutableStateOf(false) }
Button(onClick = {
isServiceRunning = !isServiceRunning
if (isServiceRunning) {
onStartService()
} else {
onStopService()
}
}) {
Text(if (isServiceRunning) "Detener Servicio" else "Iniciar Servicio")
}
}
Fichero manifest
Hay que declarar el servicio en el manifest del proyecto.
Exported es para permitir o no que otra aplicación pueda ejecutar el servicio.
<manifest ...>
<application ...>
...
<service
android:name=".MyBackgroundService"
android:exported="false" />
</application>
</manifest>
Main Activity
La clase main
consta de dos funciones , una para ejecutar el servicio y otra para parar el servicio.
También muestra el botón y pasa las funciones por parámetro al composable.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Surface {
// Boton
StartStopServiceButton(
onStartService = {
startMyBackgroundService()
},
onStopService = {
stopMyBackgroundService()
}
)
}
}
}
}
private fun startMyBackgroundService() {
val intent = Intent(
this,
MyBackgroundService::class.java
)
startService(intent)
}
private fun stopMyBackgroundService() {
val intent = Intent(
this,
MyBackgroundService::class.java
)
stopService(intent)
}
}
Foreground Services
Servicio
La declaracion de un foreground service es muy similar al background service, lo unico que cambia es lo siguiente:
- Crear la notificación para mostrar al usuario
- En el método
onStartCommand
hay que ejecutar el métodostartForeground
con la notificación creada.
Para versiones iguales o superiores a Android 8 (API 26) hay que crear el canal para la notificación como en el método createNotificationChannel()
.
La notificación se crea mediante NotificationCompat.Builder
en el método createNotification()
, dicho método retorna una Notification
que la recibirá el método startForeground()
class MyForegroundService : Service() {
companion object {
const val CHANNEL_ID = "my_channel_id"
const val NOTIFICATION_ID = 1
}
override fun onCreate() {
super.onCreate()
createNotificationChannel()
}
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int): Int {
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
return START_NOT_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
// Crea el canal para la notificación
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"My Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
val notificationManager =
getSystemService(NotificationManager::class.java)
notificationManager
.createNotificationChannel(channel)
}
}
// Crea la notifcación
private fun createNotification(): Notification {
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText("Servicio en primer plano...")
.setSmallIcon(R.drawable.ic_notification_icon)
.build()
}
}
Composable
El composable recibe por parámetro una función que al hacer click en el botón se ejecuta dicha función.
@Composable
fun MyButton(onStartService: () -> Unit) {
Button(onClick = { onStartService() }) {
Text("Iniciar Foreground Service")
}
}
Fichero manifest
Para versiones iguales o superiores a la API 28 hay que añadir el permiso FOREGROUND_SERVICE
.
Cada servicio se declara dentro de application
mediante la etiqueta service
.
<manifest ... >
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE"/>
<application ...>
<service
android:name=".MyForegroundService"
android:enabled="true"
android:exported="false" />
...
</application>
</manifest>
Main Activity
La clase main
se encarga de mostrar el botón y vincular el método privado que inicia el servicio, muy parecido en el anterior ejemplo.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyButton(
onStartService = { startMyForegroundService() }
)
}
}
private fun startMyForegroundService() {
val intent = Intent(
this, MyForegroundService::class.java
)
startService(intent)
}
}
Tareas Programadas
Si necesitas gestionar notificaciones, como un aviso programado en una fecha específica, lo ideal es utilizar un WorkManager
en lugar de los servicios. Dicha funcionalidad solo está disponible para Android Jetpack.
Top comments (0)