38 votes

Où arrêter / détruire les threads dans la classe de service Android?

J'ai créé un service threadé de la manière suivante:

 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);
         }
    }
}

...
}
 

Après avoir fermé l’application, le téléphone fonctionne très lentement et j’imagine que c’est à cause d’un problème de terminaison de fil.

Est-ce que quelqu'un sait quel est le meilleur moyen de terminer tous les threads avant de mettre fin à l'application?

90voto

sooniln Points 9909

Il y a plusieurs problèmes dans l'exemple de code que vous avez posté, je vais répondre dans l'ordre:

1) Thread.stop() est obsolète depuis un certain temps maintenant, car il peut laisser des variables dépendantes dans incohérent états dans certaines circonstances. Voir ce Soleil de réponse de la page pour plus de détails. Une méthode préférée de l'arrêt et le démarrage d'un thread est comme suit:

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
  }
}

2) les mesures de la liste est accessible par plusieurs threads (l'événement de fil et votre utilisateur thread) en même temps, sans aucune synchronisation. Il semble que vous n'avez pas à rouler votre propre synchronisation, vous pouvez utiliser un BlockingQueue.

3) Vous êtes la création d'un nouveau Socket à chaque itération de l'envoi de votre Fil. C'est plutôt un poids lourd de l'opération, et n'a vraiment de sens que si vous vous attendez à des mesures extrêmement rares (disons une heure ou moins). Soit vous voulez une persistance de la socket qui n'est pas recréés à chaque boucle du fil, ou si vous voulez un one shot exécutables que vous pouvez "fire and forget", qui crée une socket, envoie toutes les données pertinentes, et de finitions. (Une note rapide sur l'utilisation d'une persistance de la Socket, socket méthodes qui bloquent, telles que la lecture, ne peut pas être interrompu par un Fil.interrupt(), et ainsi, lorsque vous voulez arrêter le fil, vous devez fermer le socket ainsi que l'appel de l'interruption)

4) Il y a peu de point de lancer vos propres exceptions au sein d'un Thread, à moins que vous attendez pour l'attraper quelque part d'autre. Une meilleure solution consiste à enregistrer l'erreur et si il est irrécupérable, arrêter le fil. Un thread peut s'arrêter de lui-même avec un code comme (dans le même contexte que ci-dessus):

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
      }
    }
  }

Enfin, si vous voulez être sûr qu'un thread se termine avec le reste de votre application, n'importe quoi, une bonne technique est d'appeler Fil.setDaemon(vrai) après sa création et avant de démarrer le thread. Il signale le fil à fil de démon, sens de la VM sera de s'assurer qu'il est automatiquement détruit si il n'y a pas de non-démon de threads en cours d'exécution (par exemple si votre application se ferme).

Obéissant à de meilleures pratiques en ce qui concerne les Threads doivent s'assurer que votre application ne permet pas de bloquer ou de ralentir le téléphone, s'ils peuvent être assez complexes :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X