android - siempre - Continuar con el servicio incluso si la aplicación se borra de la aplicación reciente
servicio siempre activo android studio (6)
Estoy teniendo un pequeño problema
En mi aplicación, se inicia un Servicio después de que el usuario haya iniciado sesión correctamente.
Anteriormente, el servicio debía detenerse si se cancelaba la aplicación.
(por ejemplo, eliminado de la lista de aplicaciones recientes al deslizar). Así que usamos
android:stopWithTask="true"
.
Ahora necesitamos que el Servicio se ejecute tal como está, incluso si la Tarea que lo inició, se elimina de la lista de Aplicaciones recientes.
Así que cambié el Servicio para incluir
android:stopWithTask="false"
.
Pero eso no parece funcionar.
Código relacionado:
Aquí está la parte manifiesta relacionada con el Servicio:
<service
android:enabled="true"
android:name=".MyService"
android:exported="false"
android:stopWithTask="false" />
En MyService.java:
public class MyService extends AbstractService {
@Override
public void onStartService() {
Intent intent = new Intent(this, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new Notification(R.drawable.ic_launcher, "My network services", System.currentTimeMillis());
notification.setLatestEventInfo(this, "AppName", "Message", pendingIntent);
startForeground(MY_NOTIFICATION_ID, notification);
}
@Override
public void onTaskRemoved(Intent rootIntent) {
Toast.makeText(getApplicationContext(), "onTaskRemoved called", Toast.LENGTH_LONG).show();
System.out.println("onTaskRemoved called");
super.onTaskRemoved(rootIntent);
}
}
AbstractService.java
es una clase personalizada que extiende
Sevrice
:
public abstract class AbstractService extends Service {
protected final String TAG = this.getClass().getName();
@Override
public void onCreate() {
super.onCreate();
onStartService();
Log.i(TAG, "onCreate(): Service Started.");
}
@Override
public final int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStarCommand(): Received id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
@Override
public final IBinder onBind(Intent intent) {
return m_messenger.getBinder();
}
@Override
public void onDestroy() {
super.onDestroy();
onStopService();
Log.i(TAG, "Service Stopped.");
}
public abstract void onStartService();
public abstract void onStopService();
public abstract void onReceiveMessage(Message msg);
@Override
public void onTaskRemoved(Intent rootIntent) {
Toast.makeText(getApplicationContext(), "AS onTaskRemoved called", Toast.LENGTH_LONG).show();
super.onTaskRemoved(rootIntent);
}
}
Ahora, si inicio sesión en la aplicación, se inicia MyService. Después de eso presiono el botón de Inicio, por lo que la aplicación se mueve al fondo. Ahora elimino la aplicación de la lista de aplicaciones recientes. En ese momento, debería ver el mensaje Toast y la consola, según la descripción de este método:
public void onTaskRemoved (Intento rootIntent)
Agregado en API nivel 14
Esto se llama si el servicio se está ejecutando actualmente y el usuario ha eliminado una tarea que proviene de la aplicación del servicio. Si ha configurado ServiceInfo.FLAG_STOP_WITH_TASK, no recibirá esta devolución de llamada; en cambio, el servicio simplemente se detendrá.
Parámetros rootIntent Intento raíz original que se utilizó para iniciar la tarea que se está eliminando.
Pero no estoy viendo nada de eso.
El servicio regresa START_STICKY en
onStartCommand
, por lo que creo que
onTaskRemoved
debería
onTaskRemoved
junto con el indicador
android:stopWithTask="false"
.
¿Me estoy perdiendo algo?
Avíseme en caso de que necesite agregar algún código que podría ser importante para descubrir qué está mal.
PD: Probé esto en 4.2.2 hasta ahora.
PD: acabo de probar el mismo código en 4.1.2, en el que el Servicio sigue ejecutándose, y también recibo el mensaje "onTaskRemoved llamado" en el registro.
¿Qué debo hacer para que esto funcione en todas las versiones?
En su servicio, agregue el siguiente código. Me funciona bien en 4.4.2
Aquí hay una solución que encontré y funciona bien para reiniciar un servicio si su proceso se cierra al cerrar la aplicación.
@Override public void onTaskRemoved(Intent rootIntent){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
PendingIntent restartServicePendingIntent = PendingIntent.getService(
getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmService.set(ELAPSED_REALTIME, elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
Escriba las 5 líneas que agregué en
oncreate()
de la clase de servicio
Me gusta esto:
public class AlarmService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Intent iHeartBeatService = new Intent(AlarmService.this,
AlarmService.class);
PendingIntent piHeartBeatService = PendingIntent.getService(this, 0,
iHeartBeatService, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(piHeartBeatService);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), 1000, piHeartBeatService);
}
}
o
prueba este
public class MyService extends Service{
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
System.out.println("service created");
}
@SuppressLint("NewApi")
@Override
public void onTaskRemoved(Intent rootIntent) {
// TODO Auto-generated method stub
System.out.println("onTaskRemoved");
super.onTaskRemoved(rootIntent);
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
System.out.println("Service started");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Service is running");
}
}, 5000);
}
}
Parece que quitar una aplicación de las ''tareas recientes'' mata todo lo adjunto.
Tal vez debería echar un vistazo allí para encontrar una manera de relanzar su servicio si se detiene: https://.com/a/22464640/4232337
Si está bien poner una notificación mientras el servicio se está ejecutando, puede usar startForegroundService y startForeground para lograrlo.
Hay tres trucos importantes:
- Llame a startForegroundService que crea un servicio de larga ejecución no limitado al contexto enlazado y haga una promesa de llamar a startForeground más tarde.
- Regrese START_STICKY en onStartComand
- Llame a startForeground con una notificación como se prometió en (1).
Por ejemplo, si desea ejecutar un TimerService, en su TimerActivity hará lo siguiente:
private var timerService: TimerService? = null
private val timerServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as TimerService.Binder
timerService = binder.getService()
}
override fun onServiceDisconnected(arg0: ComponentName) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
...
startButton.setOnClickListener {
timerService?.startTimer(60L, 0L)
}
}
override fun onStart() {
super.onStart()
Intent(this, TimerService::class.java).also {
ContextCompat.startForegroundService(this, it) // that''s the first trick
bindService(it, timerServiceConnection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
unbindService(timerServiceConnection)
timerService?.updateNotification(secondsRemaining)
}
Su TimerService será algo así:
class TimerService : Service() {
private val binder = Binder()
private var serviceLooper: Looper? = null
private var serviceHandler: ServiceHandler? = null
private var timer: CountDownTimer? = null
private val notificationUtil by lazy {
NotificationUtil(this)
}
override fun onCreate() {
HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
start()
serviceLooper = looper
serviceHandler = ServiceHandler(looper)
}
}
override fun onBind(intent: Intent?): IBinder? = binder
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val timerRemaining = intent?.getLongExtra(EXTRA_REMAINING, 0) ?: 0L
if (timerRemaining != 0L) {
serviceHandler?.obtainMessage()?.also { msg ->
msg.arg1 = startId
msg.data.putLong(EXTRA_REMAINING, timerRemaining)
serviceHandler?.sendMessage(msg)
}
}
return START_STICKY // that''s the second trick
}
override fun onDestroy() {
super.onDestroy()
timer?.cancel()
}
fun startTimer(secondsRemaining: Long, id: Long) {
updateNotification(secondsRemaining)
Intent(this, TimerService::class.java).apply {
putExtra(EXTRA_REMAINING, secondsRemaining)
}.also {
onStartCommand(it, 0, id.toInt())
}
}
fun stopTimer() {
timer?.cancel()
}
fun updateNotification(secondsRemaining: Long){
val notification = NotificationCompat.Builder(this, NotificationUtil.CHANNEL_ID_TIMER)
.setSmallIcon(R.drawable.ic_timer)
.setAutoCancel(true)
.setDefaults(0)
.setContentTitle(secondsRemaining.formatSeconds())
.setContentText("Timer")
.setContentIntent(notificationUtil.getPendingIntentWithStack(this, TimerActivity::class.java))
.setOngoing(true)
.build()
startForeground(NotificationUtil.NOTIFICATION_ID, notification) // that''s the last trick
}
private fun sendMessage(remaining: Long) {
Intent(TimerService::class.java.simpleName).apply {
putExtra(EXTRA_REMAINING, remaining)
}.also {
LocalBroadcastManager.getInstance(this).sendBroadcast(it)
}
}
private inner class ServiceHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
val secondsRemaining = msg.data.getLong(EXTRA_REMAINING)
notificationUtil.showTimerStarted(secondsRemaining)
timer = object : CountDownTimer(secondsRemaining * 1000, 1000) {
override fun onTick(millisUntilFinished: Long) {
Log.i(this::class.java.simpleName, "tick ${(millisUntilFinished / 1000L).formatSeconds()}")
updateNotification(millisUntilFinished / 1000)
sendMessage(millisUntilFinished / 1000)
}
override fun onFinish() {
Log.i(this::class.java.simpleName, "finish")
notificationUtil.showTimerEnded()
sendMessage(0)
stopSelf()
}
}.start()
}
}
inner class Binder : android.os.Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): TimerService = this@TimerService
}
companion object {
const val EXTRA_REMAINING = "EXTRA_REMAINING"
const val NOTIFICATION_ID = 1 // cannot be 0
fun Long.formatSeconds(): String {
val s = this % 60
val m = this / 60 % 60
val h = this / (60 * 60) % 24
return if (h > 0) String.format("%d:%02d:%02d", h, m, s)
else String.format("%02d:%02d", m, s)
}
}
}
Si se une a su servicio desde una subclase de clase de Aplicación y mantiene su conexión de IBinder, el servicio permanecerá activo incluso después de que la aplicación se elimine de las aplicaciones recientes.
Simplemente siga estos escenarios, su servicio y procesos (los hilos se ejecutan dentro de su servicio) permanecerán continuos.
-
Cree el servicio y use START_STICKY como valor de retorno en el método onStartCommand como se muestra a continuación:
@Override public int onStartCommand(final Intent intent, final int flags, final int startId) { //your code return START_STICKY; }
-
El código anterior reiniciará el servicio si se destruye y siempre se mantendrá en ejecución, pero el proceso (subprocesos) ejecutado desde el servicio dejará de funcionar si su aplicación se elimina de las aplicaciones recientes. Para garantizar que sus procesos (subprocesos) permanezcan siempre en condiciones de ejecución, debe anular el método onTaskRemoved () y agregar código para reiniciar las tareas como se muestra a continuación.
@Override public void onTaskRemoved(Intent rootIntent){ Intent restartServiceTask = new Intent(getApplicationContext(),this.getClass()); restartServiceTask.setPackage(getPackageName()); PendingIntent restartPendingIntent =PendingIntent.getService(getApplicationContext(), 1,restartServiceTask, PendingIntent.FLAG_ONE_SHOT); AlarmManager myAlarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE); myAlarmService.set( AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartPendingIntent); super.onTaskRemoved(rootIntent); }
- Iniciar servicio como a continuación
startService (nueva intención (esto, YourService.class));