2009-03-25 5 views
36

Я создал Резьбовое-сервис следующим образом:Где остановиться/уничтожить потоки в 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); 
     } 
    } 
} 

... 
} 

После того как я закрыть приложение телефон работает очень медленно, и я предполагаю, что это происходит из-за нитку отказ терминации.

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

ответ

89

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

Есть несколько проблем в образце кода вы публикуемую обращусь в порядке:

1) Thread.stop() является устаревшим в течение довольно продолжительного времени, так как она может оставить зависимые переменные в несовместимых состояний некоторые обстоятельства. См. для получения дополнительной информации (Edit: эта ссылка сейчас мертва, см. this page for why not to use 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 
    } 
} 

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

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

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

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

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 (например, если ваше приложение завершает работу).

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

+0

Все это происходит после завершения цикла while. Это не останавливает поток потока потока, который вы ожидаете от interupt. В основном вы можете добиться того же, установив булевский флаг. 'continueRunning = false;' with 'while (continueRunning);' – Doomsknight

+0

Привет, как это реализовать в пользовательском потоке, который простирается от Thread? вы можете показать некоторые примеры. – bman

+0

Может ли кто-нибудь объяснить цель этого сравнения Thread.currentThread() == runner? –

6

На самом деле, вам не нужно «бегун» переменная как описано выше, что-то вроде:

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

Но вообще, сидя в Thread.sleep() петли действительно плохая идея.

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