¿Cómo forzar que un IntentService se detenga inmediatamente con un botón cancelar de una actividad?


Tengo un IntentService que se inicia desde una Actividad y me gustaría poder detener el servicio inmediatamente desde la actividad con un botón "cancelar" en la actividad. Tan pronto como se presione el botón "cancelar", quiero que el servicio deje de ejecutar líneas de código.

He encontrado una serie de preguntas similares a esta (es decir, aquí, aquí, aquí, aquí), pero no hay respuestas buenas. Activity.stopService() y Service.stopSelf() ejecutan el método Service.onDestroy() inmediatamente pero luego deje que el código en onHandleIntent() termine todo el camino antes de destruir el servicio.

Dado que aparentemente no hay una forma garantizada de terminar el subproceso del servicio inmediatamente, la única solución recomendada que puedo encontrar ( aquí) es tener una variable miembro booleana en el servicio que se pueda cambiar en el método onDestroy(), y luego tener casi cada línea del código en onHandleIntent() envuelta en su propia cláusula "if" mirando esa variable. Esa es una manera horrible de escribir codificar.

¿Alguien sabe de una mejor manera de hacer esto en un IntentService?

Author: Steven Vascellaro, 2012-06-29

9 answers

Detener un hilo o un proceso inmediatamente es a menudo una cosa sucia. Sin embargo, debería estar bien si su servicio es apátrida.

Declare el servicio como un proceso separado en el manifiesto:

<service
     android:process=":service"
     ...

Y cuando quieras detener su ejecución, simplemente mata ese proceso:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();

Iterator<RunningAppProcessInfo> iter = runningAppProcesses.iterator();

while(iter.hasNext()){
    RunningAppProcessInfo next = iter.next();

    String pricessName = getPackageName() + ":service";

    if(next.processName.equals(pricessName)){
        Process.killProcess(next.pid);
        break;
    }
}
 10
Author: kupsef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-29 10:02:28

Aquí está el truco, hacer uso de una variable estática volátil y comprobar continuar condición en algunas de las líneas en su servicio que el servicio continuar debe ser comprobado:

class MyService extends IntentService {
    public static volatile boolean shouldContinue = true;
    public MyService() {
        super("My Service");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        doStuff();
    }

    private void doStuff() {
        // do something 

        // check the condition
        if (shouldContinue == false) {
            stopSelf();
            return;
        }

       // continue doing something

       // check the condition
       if (shouldContinue == false) {
           stopSelf();
           return;
       }

       // put those checks wherever you need
   }
}

Y en su actividad hacer esto para detener su servicio,

 MyService.shouldContinue = false;
 29
Author: Sadegh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-14 16:13:15

He utilizado un BroadcastReceiver dentro del servicio que simplemente pone un booleano stop a true. Ejemplo:

private boolean stop=false;

public class StopReceiver extends BroadcastReceiver {

   public static final String ACTION_STOP = "stop";

   @Override
   public void onReceive(Context context, Intent intent) {
       stop = true;
   }
}


@Override
protected void onHandleIntent(Intent intent) {
    IntentFilter filter = new IntentFilter(StopReceiver.ACTION_STOP);
    filter.addCategory(Intent.CATEGORY_DEFAULT);
    StopReceiver receiver = new StopReceiver();
    registerReceiver(receiver, filter);

    // Do stuff ....

    //In the work you are doing
    if(stop==true){
        unregisterReceiver(receiver);
        stopSelf();
    }
}

Luego, desde la llamada a la actividad:

//STOP SERVICE
Intent sIntent = new Intent();
sIntent.setAction(StopReceiver.ACTION_STOP);
sendBroadcast(sIntent);

Para detener el servicio.

PD: Uso un booleano porque en mi caso detengo el servicio mientras estoy en un bucle, pero probablemente puedes llamar a unregisterReceiver y stopSelf en onReceive.

PD2: No olvide llamar a unregisterReceiver si el servicio termina su trabajo normalmente o obtendrá un IntentReceiver filtrado error.

 4
Author: JoeyCK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-20 10:28:34

En el caso de IntentService no se detiene ni toma ninguna otra solicitud a través de alguna acción de intención hasta que su método onHandleIntent complete la solicitud anterior.

Si intentamos iniciar IntentService de nuevo con alguna otra acción, onHandleIntent se llamará solo cuando se haya terminado la intent / tarea anterior.

También stopService(intent); o stopSelf(); no funciona hasta que el método onHandleIntent() termina su tarea.

Así que creo que aquí la mejor solución es usar normal Service aquí.

Espero que ¡ayudará!

 2
Author: Manish DEWAN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-30 08:12:51

Si usas un IntentService, entonces creo que estás atascado haciendo algo como lo describes, donde el código onHandleIntent() tiene que sondear su señal de "stop".

Si su tarea en segundo plano es potencialmente de larga duración, y si necesita ser capaz de detenerla, creo que es mejor usar un Service simple en su lugar. En un alto nivel, escriba su Servicio a:

  • Exponer una Intent" start " para iniciar una AsyncTask para realizar su trabajo en segundo plano, guardando una referencia a esa nueva creación AsyncTask.
  • Expone una Intención de "cancelar" para invocar AsyncTask.cancel(true), o hacer que onDestroy invoque AsyncTask.cancel(true).
  • La Actividad puede entonces enviar la Intent "cancelar" o simplemente llamar a stopService().

A cambio de la capacidad de cancelar el trabajo en segundo plano, el Servicio asume las siguientes responsabilidades:

  • El AsyncTask doInBackground() tendrá que manejar con gracia la excepción interrumpida y/o verificar periódicamente si hay Subprocesos.interrumpida (), y retorno "temprano".
  • El Servicio tendrá que asegurarse de que stopSelf() es llamado (tal vez en AsyncTask onPostExecute/onCancelled).
 1
Author: rtsai2000,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-02 21:54:51
@Override
protected void onHandleIntent(Intent intent) {
    String action = intent.getAction();
    if (action.equals(Action_CANCEL)) {
        stopSelf();
    } else if (action.equals(Action_START)) {
        //handle
    }
}

Espero que funcione.

 0
Author: cycDroid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-30 12:55:12

Como @budius ya mencionó en su comentario, debe establecer un booleano en el Service cuando haga clic en ese botón:

// your Activity.java
public boolean onClick() {
   //...
   mService.performTasks = false;
   mService.stopSelf();
}

Y en su manejo Intent, antes de hacer la importante tarea de confirmar / enviar la información de intent, simplemente use ese booleano:

// your Service.java
public boolean performTasks = true;

protected void onHandleIntent(Intent intent) {
   Bundle intentInfo = intent.getBundle();
   if (this.performTasks) {
      // Then handle the intent...
   }
}

De lo contrario, el Servicio hará su tarea de procesamiento Intent. Así es como estaba destinado a ser utilizado, porque no puedo ver cómo se podría resolver de otra manera si nos fijamos en el núcleo codificar.

 0
Author: tolgap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-02 22:30:18

Aquí hay un código de ejemplo para iniciar/detener el servicio

Para empezar,

Intent GPSService = new Intent(context, TrackGPS.class);
context.startService(GPSService);

Para parar,

context.stopService(GPSService);

 -1
Author: Rinkal Bhanderi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-29 07:59:51
context.stopService(GPSService);
 -1
Author: Pooja Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-13 10:49:19