¿Dónde detener / destruir los hilos en la clase de servicio de Android?

Creé un servicio enhebrado de la siguiente manera:

public class TCPClientService extends Service{ ... @Override public void onCreate() { ... Measurements = new LinkedList(); enableDataSending(); } @Override public IBinder onBind(Intent intent) { //TODO: Replace with service binding implementation return null; } @Override public void onLowMemory() { Measurements.clear(); super.onLowMemory(); } @Override public void onDestroy() { Measurements.clear(); super.onDestroy(); try { SendDataThread.stop(); } catch(Exception e){ ... } } private Runnable backgrounSendData = new Runnable() { public void run() { doSendData(); } }; private void enableDataSending() { SendDataThread = new Thread(null, backgrounSendData, "send_data"); SendDataThread.start(); } private void addMeasurementToQueue() { if(Measurements.size() <= 100) { String measurement = packData(); Measurements.add(measurement); } } private void doSendData() { while(true) { try { if(Measurements.isEmpty()) { Thread.sleep(1000); continue; } //Log.d("TCP", "C: Connecting..."); Socket socket = new Socket(); socket.setTcpNoDelay(true); socket.connect(new InetSocketAddress(serverAddress, portNumber), 3000); //socket.connect(new InetSocketAddress(serverAddress, portNumber)); if(!socket.isConnected()) { throw new Exception("Server Unavailable!"); } try { //Log.d("TCP", "C: Sending: '" + message + "'"); PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())),true); String message = Measurements.remove(); out.println(message); Thread.sleep(200); Log.d("TCP", "C: Sent."); Log.d("TCP", "C: Done."); connectionAvailable = true; } catch(Exception e) { Log.e("TCP", "S: Error", e); connectionAvailable = false; } finally { socket.close(); announceNetworkAvailability(connectionAvailable); } } catch (Exception e) { Log.e("TCP", "C: Error", e); connectionAvailable = false; announceNetworkAvailability(connectionAvailable); } } } ... } 

Después de cerrar la aplicación, el teléfono funciona realmente lento y supongo que se debe a una falla en la terminación del hilo.

¿Alguien sabe cuál es la mejor manera de terminar todos los hilos antes de finalizar la aplicación?

Anexo : El marco de Android proporciona muchos ayudantes para trabajos únicos, trabajos en segundo plano, etc., que pueden ser preferibles a tratar de rodar su propio hilo en muchos casos. Como se menciona en una publicación a continuación, AsyncTask es un buen punto de partida para mirar. Animo a los lectores a mirar primero las disposiciones del marco antes incluso de comenzar a pensar en hacer su propio enhebrado.

Hay varios problemas en la muestra de código que ha publicado que abordaré en orden:

1) Thread.stop () ha quedado obsoleto desde hace bastante tiempo, ya que puede dejar variables dependientes en estados inconsistentes en algunas circunstancias. Consulte esta página de respuesta de Sun para obtener más detalles (Editar: ese enlace ahora está muerto, consulte esta página para saber por qué no usar Thread.stop () ). Un método preferido para detener e iniciar un hilo es el siguiente (suponiendo que el hilo se ejecutará de forma indefinida):

 private volatile Thread runner; public synchronized void startThread(){ if(runner == null){ runner = new Thread(this); runner.start(); } } public synchronized void stopThread(){ if(runner != null){ Thread moribund = runner; runner = null; moribund.interrupt(); } } public void run(){ while(Thread.currentThread() == runner){ //do stuff which can be interrupted if necessary } } 

Este es solo un ejemplo de cómo detener un hilo, pero la conclusión es que usted es responsable de salir de un hilo como lo haría con cualquier otro método. Mantenga un método de comunicación de hilos cruzados (en este caso una variable volátil, también podría ser a través de un mutex, etc.) y dentro de su lógica de hilos, utilice ese método de comunicación para verificar si debe salir, limpiarse, etc.

2) A su lista de medidas se accede mediante varios hilos (el hilo del evento y su hilo de usuario) al mismo tiempo sin ninguna sincronización. Parece que no tiene que pasar su propia sincronización, puede usar una BlockingQueue .

3) Está creando un nuevo Socket cada iteración de su Thread que envía. Esta es una operación bastante pesada, y solo tiene sentido si espera que las mediciones sean extremadamente infrecuentes (digamos una por hora o menos). O bien, si desea un socket persistente que no se vuelva a crear en cada ciclo del hilo, o si desea ejecutar un disparo, puede “disparar y olvidar”, lo que crea un socket, envía todos los datos relevantes y finaliza. (Una nota rápida sobre el uso de un zócalo persistente, los métodos de zócalo que bloquean, como la lectura, no pueden ser interrumpidos por Thread.interrupt (), por lo que cuando desee detener el hilo, debe cerrar el zócalo y llamar a interrupción)

4) No tiene mucho sentido arrojar sus propias excepciones desde dentro de un subproceso, a menos que espere atraparlo en otro lugar. Una mejor solución es registrar el error y si es irrecuperable, detenga el hilo. Un hilo puede detenerse con un código similar (en el mismo contexto que el anterior):

 public void run(){ while(Thread.currentThread() == runner){ //do stuff which can be interrupted if necessary if(/*fatal error*/){ stopThread(); return; //optional in this case since the loop will exit anyways } } } 

Finalmente, si quiere estar seguro de que un hilo sale con el rest de su aplicación, sin importar qué, una buena técnica es llamar a Thread.setDaemon (true) después de la creación y antes de iniciar el hilo. Esto marca el hilo como un hilo de daemon, lo que significa que la máquina virtual se asegurará de que se destruya automáticamente si no se están ejecutando subprocesos no daemon (como si la aplicación se cierra).

Obedecer las mejores prácticas con respecto a los Threads debería garantizar que tu aplicación no cuelgue o ralentice el teléfono, aunque pueden ser bastante complejas 🙂

En realidad, no necesita la variable “corredor” como se describió anteriormente, algo así como:

 while (!interrupted()) { try { Thread.sleep(1000); } catch (InterruptedException ex) { break; } } 

Pero, en general, sentarse en un bucle Thread.sleep () es una muy mala idea.

Mire la API AsyncTask en la nueva 1.5 API. Probablemente resolverá su problema más elegantemente que usar un servicio. Su teléfono se está volviendo lento porque el servicio nunca se apaga; no hay nada que cause que el servicio se mate solo.