Encontré un comportamiento en la gestión de procesos de Android junto con los servicios en primer plano, que realmente me confunde.
Lo que es razonable para mi
- Cuando desliza su aplicación desde 'Aplicaciones recientes', el sistema operativo debe finalizar el proceso de la aplicación en un futuro relativamente cercano.
- Cuando desliza su aplicación desde 'Aplicaciones recientes' mientras ejecuta un servicio en primer plano, la aplicación se mantiene activa.
- Cuando detiene el servicio en primer plano antes de deslizar su aplicación desde 'Aplicaciones recientes', obtiene lo mismo que para 1).
Lo que me confunde
Cuando detiene el servicio en primer plano sin tener actividades en primer plano (la aplicación NO aparece en 'Aplicaciones recientes'), esperaría que la aplicación se elimine ahora.
Sin embargo, esto no está sucediendo, el proceso de la aplicación sigue vivo.
Ejemplo
He creado un ejemplo mínimo que muestra este comportamiento.
El servicio de primer plano:
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import timber.log.Timber
class MyService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() {
super.onCreate()
Timber.d("onCreate")
}
override fun onDestroy() {
super.onDestroy()
Timber.d("onDestroy")
// just to make sure the service really stops
stopForeground(true)
stopSelf()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Timber.d("onStartCommand")
startForeground(ID, serviceNotification())
return START_NOT_STICKY
}
private fun serviceNotification(): Notification {
createChannel()
val stopServiceIntent = PendingIntent.getBroadcast(
this,
0,
Intent(this, StopServiceReceiver::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("This is my service")
.setContentText("It runs as a foreground service.")
.addAction(0, "Stop", stopServiceIntent)
.build()
}
private fun createChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(
NotificationChannel(
CHANNEL_ID,
"Test channel",
NotificationManager.IMPORTANCE_DEFAULT
)
)
}
}
companion object {
private const val ID = 532207
private const val CHANNEL_ID = "test_channel"
fun newIntent(context: Context) = Intent(context, MyService::class.java)
}
}
BroadcastReceiver para detener el servicio:
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class StopServiceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val serviceIntent = MyService.newIntent(context)
context.stopService(serviceIntent)
}
}
La actividad:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startService(MyService.newIntent(this))
}
}
El manifiesto:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.christophlutz.processlifecycletest">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyService"/>
<receiver android:name=".StopServiceReceiver" />
</application>
</manifest>
Pruébelo de las siguientes maneras:
- Inicie la aplicación, detenga el servicio en primer plano, elimine la aplicación de 'Aplicaciones recientes'
- Inicie la aplicación, elimine la aplicación de 'Aplicaciones recientes', detenga el servicio en primer plano
Puede ver en LogCat de Android Studio que el proceso de la aplicación está marcado [MUERTO] para el caso 1 pero no para el caso 2.
Dado que es bastante fácil de reproducir, podría ser un comportamiento previsto, pero no encontré ninguna mención real de esto en los documentos.
¿Alguien sabe lo que está pasando aquí?
onDestroy
Se llama a (Servicio), pero el proceso permanece vivo mucho después de que todo se haya ido. Incluso si el sistema operativo mantenía vivo el proceso en caso de que el servicio se reiniciara, no veo por qué no hace lo mismo cuando se detiene el servicio primero, luego se elimina la aplicación de los recientes. El comportamiento mostrado parece bastante poco intuitivo, especialmente dados los cambios recientes en los límites de ejecución en segundo plano, por lo que sería bueno saber cómo garantizar que el proceso se detenga