¿Cómo forzar a un IntentService a detenerse inmediatamente con un botón de cancelación de una actividad?

Tengo un IntentService que se inicia desde una actividad y me gustaría poder detener el servicio inmediatamente de 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 esto (es decir, aquí , aquí , aquí , aquí ), pero no hay buenas respuestas. Activity.stopService() y Service.stopSelf() ejecutan el método onHandleIntent() inmediatamente, pero luego deje que el código en onHandleIntent() termine completamente antes de destruir el servicio.

Como aparentemente no hay manera garantizada de terminar el hilo del servicio de inmediato, la única solución recomendada que puedo encontrar ( aquí ) es tener una variable miembro booleana en el servicio que se puede cambiar en el método onDestroy() , y luego tienen casi Cada línea del código en onHandleIntent() envuelto en su propia cláusula "if" mirando esa variable. Es una forma horrible de escribir código.

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

Parar un thead / proceso immediatelly es siempre una cosa sucia. Sin embargo, debería estar bien si su servicio es apátrida.

Declarar el servicio como un proceso separado en el manifiesto:

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

Y cuando quiera detener su ejecución, simplemente mate 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; } } 

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

 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; 

He utilizado un BroadcastReceiver dentro del servicio que simplemente pone una parada booleana 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, de la llamada a la actividad:

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

Para detener el servicio.

PD: Utilizo un boolean porque en mi caso paro el servicio mientras que en un bucle pero usted puede probablemente llamar el unregisterReceiver y para sí mismo en onReceive.

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

En caso de IntentService no se detiene o toma cualquier 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 nuevamente con alguna otra acción, onHandleIntent será llamado sólo cuando la intención / tarea anterior haya terminado.

También stopService(intent); O stopSelf(); No funciona hasta que el método onHandleIntent() termine su tarea.

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

¡Espero que ayude!

Si utiliza un IntentService , entonces creo que está atascado haciendo algo como usted describe, donde el código onHandleIntent() tiene que buscar su señal "stop".

Si su tarea de fondo es potencialmente de larga duración, y si necesita ser capaz de detenerlo, creo que es mejor utilizar un Service sencillo Service lugar. En un nivel alto, escriba su Servicio a:

  • Exponer un "inicio" Propósito de iniciar un AsyncTask para realizar su trabajo de fondo, guardando una referencia a ese AsyncTask recién creado.
  • Exponer una "cancelar" Intención de invocar AsyncTask.cancel(true) , o tener onDestroy () invocar AsyncTask.cancel(true) .
  • La Actividad puede entonces enviar la Intención "cancelar" o simplemente llamar a stopService ().

A cambio de la capacidad de cancelar el trabajo de fondo, el Servicio asume las siguientes responsabilidades:

  • El AsyncTask doInBackground() tendrá que manejar doInBackground() InterruptedException y / o revisar periódicamente para Thread.interrupted () y devolver "early".
  • El servicio tendrá que asegurarse de que stopSelf() se llama (quizás en AsyncTask onPostExecute / onCancelled).
 @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.

Como @ududius ya se 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 de Intent , antes de realizar la tarea importante de enviar / enviar la información de intención, 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 procesar esa Intent . Eso es lo que se pretende que se utiliza, porque no puedo ver cómo se puede solucionar de otra manera si se mira el código de núcleo.

He aquí algunos ejemplos de código para iniciar / detener el servicio

Para comenzar,

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

Para detener,

context.stopService(GPSService);

 context.stopService(GPSService); 
  • Enviar una transmisión sólo a actividades específicas
  • Android: IntentService no hace cola correctamente
  • Android: El servicio de intento anidado no se inicia después de llamar a Context.startService ()
  • ¿Cómo puedo mantener vivo el subproceso de un IntentService?
  • ¿Debo usar AsyncTask o IntentService para mi aplicación?
  • StartForeground para IntentService
  • Android la diferencia entre onHandleIntent & onStartCommand
  • Evitar que el "proceso de Android" sea un error "incorrecto"
  • Realizar una llamada REST API de un IntentService o un AsyncTask?
  • Utilizar Parcelable con un objeto con Hashmap
  • Iniciar IntentService varias veces android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.