android - services - Detener/destruir un hilo
threadpool android (7)
Tengo un servicio que lanza un Thread y un Runnable como tal.
t = new Thread(new Runnable() {
public void run() {
doSomething();
}
});
t.start();
El motivo del subproceso es realizar una tarea Async doSomething () . Por ahora no nos preocupemos por la otra clase de AsyncTask. Lo he probado y no funciona para mi caso. Edición: no puedo usar AsyncTask porque está destinado solo al subproceso de la interfaz de usuario. Este código tiene que operar dentro de un Servicio, así que no, no AsyncTask :(
doSomething () contiene algunas librerías externas, por lo que el problema que tengo es que potencialmente se puede colgar en uno de los comandos, sin devolver ningún valor (por lo tanto, no se puede realizar una comprobación de errores)
Para solucionar este problema, desearé, en algún momento, destruir el Servicio.
stopService(new Intent("net.MyService.intent));
Esto funciona bien y se verifica fácilmente en el teléfono. Sin embargo, el subproceso que se creó anteriormente continuará ejecutándose incluso cuando el Servicio que lo generó se destruya.
Por lo tanto, estoy buscando los comandos correctos para insertar en el servicio onDestroy () que limpiará el hilo para mí.
t.destroy();
t.stop();
Se deprecian y provocan fallos de aplicación.
Tomé este código de alguna parte
@Override
public void onDestroy() {
Thread th = t;
t = null;
th.interrupt();
super.onDestroy();
}
pero todavía no funciona, el hilo continúa ejecutándose. ¿Alguna ayuda chicos?
¿ Verificó la documentación de desprendimiento de primitivas de subprocesos de Java a la que se hace referencia en el APIDoc de Thread API? Encontrará algunos consejos para manejar su problema.
¿Por qué no usas una AsyncTask ?
Una tarea se puede cancelar en cualquier momento invocando cancel (booleano). Invocar este método hará que las llamadas subsiguientes a isCancelled () devuelvan verdadero. Después de invocar este método, se invocará onCancelled (Object), en lugar de onPostExecute (Object) después de que devuelva doInBackground (Object []). Para asegurarse de que una tarea se cancele lo más rápido posible, siempre debe verificar el valor de retorno de isCancelled () periódicamente desde doInBackground (Objeto []), si es posible (dentro de un bucle, por ejemplo).
Creo que la mejor manera de crear y comunicarse con otro subproceso es usar una AsyncTask . Heres un ejemplo de uno:
public class Task extends AsyncTask<Void, Void, Void> {
private static final String TAG = "Task";
private boolean mPaused;
private Runnable mRunnable;
public Task(Runnable runnable) {
mRunnable = runnable;
play();
}
@Override
protected Void doInBackground(Void... params) {
while (!isCancelled()) {
if (!mPaused) {
mRunnable.run();
sleep();
}
}
return null;
}
private void sleep() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Log.w(TAG, e.getMessage());
}
}
public void play() {
mPaused = false;
}
public void pause() {
mPaused = true;
}
public void stop() {
pause();
cancel(true);
}
public boolean isPaused() {
return mPaused;
}
}
Ahora puedes usar esta clase fácilmente y comenzar el hilo escribiendo:
Task task = new Task(myRunnable);
task.execute((Void) null);
Junto con esto, puede pausar o detener fácilmente el subproceso de bucle:
Ejemplo de pausa y reproducción del hilo:
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (task.isPaused()) {
task.play();
} else {
task.pause();
}
}
});
Ejemplo de parada y arranque del hilo:
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (task.isCancelled()) {
task = new Task(myRunnable);
task.execute((Void) null);
} else {
task.stop();
}
}
});
Es mejor usar la variable global stopThread, detener el hilo una vez que la variable se haya cambiado a verdadero.
btnStop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0){
stopThread = true;
}
});
public void run() {
while (!stopThread) {
//do something
}
}
Los métodos de destroy
y stop
hilo son intrínsecamente propensos a los puntos muertos y no son seguros. Su existencia también da la ilusión de que podría haber alguna forma de detener otro hilo inmediatamente cuando algo más se lo indique.
Entiendo que usted piensa, desde su punto de vista, este es un hilo principal, y cuando este hilo no ha recibido una respuesta de su hilo de trabajo desde hace tiempo, le gustaría matarlo y reiniciarlo, sin importar lo que está haciendo. . Pero la razón por la que esos métodos están en desuso es que debería preocuparte por lo que se hace el hilo. Mucho.
¿Qué sucede si el hilo tiene un bloqueo alrededor de una variable que debe usar más adelante? ¿Qué pasa si un hilo tiene un identificador de archivo abierto? En todos estos casos, y muchos más, simplemente detener el subproceso en su operación actual dejaría las cosas en desorden; es muy probable que su aplicación se bloquee aún más en la línea.
Por lo tanto, para que un subproceso pueda ser interrumpible o pueda cancelarse o detenerse, tiene que gestionarlo por sí mismo. Si un subproceso u operación no proporciona ninguna forma de que se interrumpa, entonces no puede interrumpirlo, se supone que hacerlo sería inseguro.
Si se puede ejecutar es literalmente
public void run() {
doSomething();
}
entonces no hay forma de interrumpirlo. Uno esperaría que si algo fuera una operación larga pudiera existir una manera de interactuar con él de manera incremental con algo como
public void run() {
while (running) {
MyParser.parseNext();
}
}
o para poder pasar una variable por referencia que indique si el hilo está interrumpido o no, y es de esperar que el método se interrumpa a sí mismo en la ubicación adecuada.
Recuerde que una operación de bloqueo está bloqueando. No hay forma de evitar eso, no puedes cancelarlo a medio camino.
Me gusta tomar el siguiente enfoque:
class MyHandler extends Handler {
final Semaphore stopEvent = new Semaphore(0);
@Override
public void handleMessage(Message msg) {
try {
while (!stopEvent.tryAcquire(0, TimeUnit.SECONDS)) {
doSomething();
if (stopEvent.tryAcquire(SLEEP_TIME, TimeUnit.MILLISECONDS)) {
break;
}
}
} catch (InterruptedException ignored) {
}
stopSelf();
}
}
En servicio onDestroy acaba de lanzar el stopEvent:
@Override
public void onDestroy() {
myHandler.stopEvent.release();
myHandler = null;
super.onDestroy();
}
Respuesta alternativa
Usa el siguiente código:
MyThread thread; // class field
Crea e inicia el hilo como lo haces ahora.
thread = new MyThread();
thread.start();
Cuando se destruye el servicio, "señal" el hilo para salir
public void onDestroy() {
// Stop the thread
thread.abort = true;
thread.interrupt();
}
Aquí está la implementación de hilo
//another class or maybe an inner class
class MyThread extends Thread {
syncronized boolean abort = false;
//ugly, I know
public void run() {
try {
if(!abort) doA();
if(!abort) doB();
if(!abort) doC();
if(!abort) doD();
} catch (InterruptedException ex) {
Log.w("tag", "Interrupted!");
}
}
}
Es posible que desee leer lo siguiente:
- ¿Cómo matas a un hilo en Java?
- Subprocesar la Depresión primitiva como ya lo señaló Claszen.
- http://www.devx.com/tips/Tip/31728 - basó mi código desde aquí, pero hay algunos problemas con el código.
Creo que podría confiar en atrapar la excepción y no verificar el aborto, pero decidí mantenerlo así.
ACTUALIZAR
He visto esta muestra en codeguru :
public class Worker implements Runnable {
private String result;
public run() {
result = blockingMethodCall();
}
public String getResult() {
return result;
}
}
public class MainProgram {
public void mainMethod() {
...
Worker worker = new Worker();
Thread thread = new Thread(worker);
thread.start();
// Returns when finished executing, or after maximum TIME_OUT time
thread.join(TIME_OUT);
if (thread.isAlive()) {
// If the thread is still alive, it''s still blocking on the methodcall, try stopping it
thread.interrupt();
return null;
} else {
// The thread is finished, get the result
return worker.getResult();
}
}
}