Ejemplo de como implementar un servicio de Android usando Kotlin y que use Foreground de Android O, mostrando la notificación permamente.
- Creación del subproceso Foreground
- Notificación permamente (requisito Android O)
- Control del servicio StartService / StopService
Preparativos
En AndroidManifest.xml deberemos añadir la directiva para poder usar servicios en Android O
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
Añadir la declaración del servicio
<service
android:name=".MyService"
android:enabled="true"
android:exported="false" />
La propiedad enabled es para activar o desactivar el servicio.
La propiedad exported si está en true el servicio puede ser llamado des de otra aplicación
Servicio en Kotlin
Creación del servicio MyService clase que se extiende de Service
class MyService : Service() {
companion object {
const val CHANNEL_ID = "ForegroundService Kotlin"
fun startService(context: Context, message: String) {
val startIntent = Intent(context, MyService::class.java)
startIntent.putExtra("inputExtra", message)
ContextCompat.startForegroundService(context, startIntent)
}
fun stopService(context: Context) {
val stopIntent = Intent(context, MyService::class.java)
context.stopService(stopIntent)
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//do heavy work on a background thread
val input: String? = intent?.getStringExtra("inputExtra")
createNotificationChannel()
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent: PendingIntent = PendingIntent.getActivity(
this,
0, notificationIntent, 0
)
val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getText(R.string.service_title))
.setContentText(input)
.setSmallIcon(R.drawable.ic_android_24dp)
.setContentIntent(pendingIntent)
.build()
startForeground(1, notification)
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(
CHANNEL_ID, "Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager: NotificationManager? = getSystemService(NotificationManager::class.java)
manager!!.createNotificationChannel(serviceChannel)
}
}
}
En está clase, se hace diferentes cosas, primero se crea el canal de notificación y las funciones para iniciar y parar el servicio, en onStartCommand es donde el subproceso (foreground service) debe lanzar la tarea a realizar, createNotificationChannel es para la creación de la notificación permanente, requisito a partir de Android Oreo incluido
Iniciando y parando el Servicio
Cuando deseamos iniciar el servicio
MyService.startService(this, "Foreground Service is running...")
Cuando deseamos parar el servicio
MyService.stopService(this)
Cómo detectar si el servicio está activo
Si se desea detectar el servicio si está activo, en kotlin podemos usar la siguiente extensión de funcionalidad
@Suppress("DEPRECATION")
fun <T> Context.isServiceRunning(service: Class<T>): Boolean {
return (getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager)
.getRunningServices(Integer.MAX_VALUE)
.any { it.service.className == service.name }
}
Su uso
if (isServiceRunning(MyService::class.java)) {
//Servicio corriendo
} else {
//Servicio parado o finalizado
}
ADB Test
Podemos usar el ADB para obtener información del servicio, con el comando
adb shell dumpsys activity services <service>
Si el servicio no está ejecutandose se obtendrá
ACTIVITY MANAGER SERVICES (dumpsys activity services)
(nothing)
Y si el servicio está corriendo y asegurar que está en primer plano, la propiedad isForeground=true
ACTIVITY MANAGER SERVICES (dumpsys activity services)
User 0 active services:
...
isForeground=true foregroundId=1
...
Código fuente
Git con el código fuente usado para el apunte