Servicios en Android🛸
Tags: android, kotlin, mobile
Published: 2023-04-19 11:30:09

- [Definición](#definición)
- [Diferencias](#diferencias)
- [Para que se usan](#para-que-se-usan)
	- [Background](#background)
	- [Foreground](#foreground)
- [Implementación](#implementación)
	- [Background Services](#background-services)
	- [Foreground Services](#foreground-services)
- [Tareas Programadas](#tareas-programadas)
- [Referencias](#referencias)

---
# 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**.



![Flow de ejecucion de los servicios en android (background services/foreground services)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e1fo13p8jkd5kewh9hnf.png)


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.


![Notificacion de un Foreground service en Android ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tz0e84ijkcs9nra4wd7c.png)


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`](https://developer.android.com/guide/topics/media/mediarecorder?hl=es-419) y video mediante [`CameraX`](https://developer.android.com/training/camerax?hl=es-419), la implementación que realicé fue un `Foreground Service` ya que `MediaRecorder` a partir del API 28 no permite el acceso al micrófono desde un `Background 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`.

```kotlin
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`

```kotlin
@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.
```xml
<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.
```kotlin
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étodo **`startForeground`** 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()`


```kotlin
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.

```kotlin
@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`](https://developer.android.com/guide/topics/manifest/service-element?hl=es-419).

```xml
<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.

```kotlin
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`](https://developer.android.com/topic/libraries/architecture/workmanager?hl=es-419)** en lugar de los servicios. Dicha funcionalidad solo está disponible para **Android Jetpack**.


# Referencias
{% embed https://developer.android.com/guide/components/services?hl=es-419  %}

{% embed https://developer.android.com/reference/android/app/Service#constants_1 %}

{% embed https://developer.android.com/guide/components/foreground-services#request-foreground-service-permissions  %}