2009-03-25 4 views
36

He creado un servicio de roscado de la siguiente manera:¿Dónde parar/destruir subprocesos en la clase de servicio de Android?

public class TCPClientService extends Service{ 
... 

@Override 
public void onCreate() { 
    ... 
    Measurements = new LinkedList<String>(); 
    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 que el teléfono funciona muy lento y supongo que se debe a la falta de terminación de hilo.

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

Respuesta

89

Addendum: Android Framework 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 envió me referiré a fin:

1) Thread.stop() está en desuso desde hace bastante tiempo, ya que puede salir de variables dependientes en estados inconsistentes en algunas circunstancias Vea this Sun answer page para más detalles (Editar: ese enlace ahora está muerto, vea this page for why not to use Thread.stop()). Un método preferido para detener e iniciar un hilo es el siguiente (suponiendo que el hilo se ejecuta un tanto 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 sólo un ejemplo de cómo detener un hilo, pero la comida para llevar 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 cruzada (en este caso una variable volátil, también podría ser mediante un mutex, etc.) y dentro de su lógica de subprocesos, use ese método de comunicación para verificar si debe salir, limpiarse, etc.

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

3) Está creando un nuevo Socket cada iteración de su Thread sending. 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 un subproceso a menos que espere encontrarlo en otro lugar. Una mejor solución es registrar el error y si es irrecuperable, detenga el hilo. Un hilo puede pararse con un código como (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 
     } 
    } 
    } 

Por último, si usted quiere estar seguro de un hilo termina con el resto de la aplicación, no importa qué, una buena técnica es para 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).

Obedeciendo las mejores prácticas con respecto a las roscas deben asegurarse de que su aplicación no se bloquea o ralentizar el teléfono, aunque pueden ser bastante complejos :)

+0

Todo lo que hace es salir del ciclo while una vez finalizado. No detiene el flujo medio del hilo, lo que esperaría que interrumpa. Básicamente puede lograr lo mismo al establecer un indicador booleano. 'continueRunning = false;' con 'while (continueRunning);' – Doomsknight

+0

Hola, ¿cómo implementar esto en un hilo personalizado que se extiende desde Thread? puedes mostrar algunos ejemplos. – bman

+0

¿Alguien puede explicar el propósito de esta comparación Thread.currentThread() == runner? –

6

En realidad, no es necesario el "corredor" variables como se describe más arriba, algo como:

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

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

Mire la API AsyncTask en la nueva API 1.5. 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.

Cuestiones relacionadas