Подтвердить что ты не робот

Где останавливать/уничтожать потоки в классе Android Service?

Я создал многопоточную службу следующим образом:

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

...
}

После того, как я закрою приложение, телефон работает очень медленно, и я думаю, что это связано с неудачей завершения потока.

Кто-нибудь знает, что является лучшим способом прекратить все потоки перед завершением работы приложения?

4b9b3361

Ответ 1

Добавление. Рамки для Android предоставляют много помощников для одноразовой работы, фоновой работы и т.д., что может быть предпочтительнее, чем пытаться перевернуть собственный поток во многих случаях. Как упоминалось в следующем сообщении, AsyncTask - хорошая отправная точка для изучения. Я призываю читателей заглянуть в рамки положений, прежде чем даже начать думать о том, чтобы делать свои собственные потоки.

В приведенном вами примере кода есть несколько проблем, которые я адресую по порядку:

1) Thread.stop() устарел уже довольно давно, так как в некоторых случаях он может оставить зависимые переменные в непоследовательных состояниях. Подробнее см. эту страницу ответа Sun (Edit: эта ссылка теперь мертва, см. на этой странице, почему бы не использовать Thread.stop()). Предпочтительным способом остановки и запуска потока является следующее (предполагая, что ваш поток будет работать несколько неопределенно):

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

Это всего лишь один пример того, как остановить поток, но выгода заключается в том, что вы несете ответственность за выход из потока, как и любой другой метод. Поддерживайте метод коммутации поперечных потоков (в этом случае переменная volatile, также может быть через мьютекс и т.д.), И в вашей логике потоков используйте этот способ связи, чтобы проверить, нужно ли вам раньше выходить, очищать и т.д.

2) В список измерений вы можете обращаться несколькими потоками (поток событий и поток пользователей) одновременно без какой-либо синхронизации. Похоже, вам не нужно откатывать собственную синхронизацию, вы можете использовать BlockingQueue.

3) Вы создаете новый Socket на каждой итерации отправляющего потока. Это довольно тяжелая операция, и только действительно имеет смысл, если вы ожидаете, что измерения будут крайне редкими (скажем, один час или меньше). Либо вы хотите, чтобы постоянный сокет, который не воссоздан в каждом цикле потока, или вы хотите запустить один выстрел, вы можете "стрелять и забывать", который создает сокет, отправляет все соответствующие данные и заканчивается. (Быстрая заметка об использовании постоянных Socket-методов сокетов, которые блокируются, например, чтение, не может быть прервана Thread.interrupt(), поэтому, когда вы хотите остановить поток, вы должны закрыть сокет, а также вызвать прерывание)

4). Существует немного смысла бросать свои собственные исключения из Thread, если вы не ожидаете, что поймаете его где-то в другом месте. Лучшим решением является регистрация ошибки, и если она невосстановима, остановите поток. Нить может остановить себя с помощью кода (в том же контексте, что и выше):

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

Наконец, если вы хотите, чтобы поток оставался с остальной частью вашего приложения, несмотря ни на что, хорошей техникой является вызов Thread.setDaemon(true) после создания и до того, как вы начнете поток. Это флага потока как поток демона, то есть виртуальная машина будет гарантировать, что она будет автоматически уничтожена, если нет потоков не-daemon (например, если ваше приложение завершает работу).

Соблюдение лучших практик в отношении Threads должно гарантировать, что ваше приложение не зависает или замедляет работу телефона, хотя они могут быть довольно сложными:)

Ответ 2

На самом деле вам не нужна переменная "runner", как описано выше, например:

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

Но, как правило, сидение в цикле Thread.sleep() - очень плохая идея.

Посмотрите на API AsyncTask в новом API 1.5. Это, скорее всего, решит вашу проблему более элегантно, чем использование сервиса. Ваш телефон замедляется, потому что служба никогда не выключается - нет ничего, что может заставить службу убить себя.