在哪里停止/销毁 Android Service 类中的线程?

Posted

技术标签:

【中文标题】在哪里停止/销毁 Android Service 类中的线程?【英文标题】:Where to stop/destroy threads in Android Service class? 【发布时间】:2010-10-15 09:02:06 【问题描述】:

我通过以下方式创建了一个线程服务:

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


...

在我关闭应用程序后,手机运行速度非常慢,我猜这是由于线程终止失败。

有谁知道在终止应用程序之前终止所有线程的最佳方法是什么?

【问题讨论】:

【参考方案1】:

附录android 框架为一次性工作、后台工作等提供了许多帮助程序,在许多情况下这可能比尝试滚动您自己的线程更可取。如下文所述,AsyncTask 是一个很好的起点。我鼓励读者在开始考虑自己的线程之前先查看框架规定。

您发布的代码示例中有几个问题我将按顺序解决:

1) Thread.stop() 已经被弃用了一段时间,因为在某些情况下它会使因变量处于不一致的状态。有关更多详细信息,请参阅this Sun answer page(编辑:该链接现已失效,请参阅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
  

这只是如何停止线程的一个示例,但要点是您负责退出线程,就像您使用任何其他方法一样。维护一个跨线程通信的方法(在这种情况下是一个 volatile 变量,也可以通过互斥锁等)并在您的线程逻辑中,使用该通信方法来检查您是否应该提前退出、清理等。

2) 您的测量列表由多个线程(事件线程和您的用户线程)同时访问,无需任何同步。看来您不必滚动自己的同步,您可以使用BlockingQueue。

3) 您在发送线程的每次迭代中都创建一个新的 Socket。这是一个相当重量级的操作,只有在您期望测量非常罕见(比如一小时或更少)时才真正有意义。要么你想要一个不会在线程的每个循环中重新创建的持久套接字,要么你想要一个一次性可运行的,你可以“触发并忘记”它创建一个套接字、发送所有相关数据并完成。 (关于使用持久 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)。这会将线程标记为守护线程,这意味着如果没有非守护线程在运行(例如,如果您的应用退出),VM 将确保自动销毁它。

遵循线程方面的最佳做法应确保您的应用不会挂起或拖慢手机速度,尽管它们可能非常复杂 :)

【讨论】:

所有这一切都是在完成后退出while循环。它不会停止线程中间流,您希望中​​断会这样做。您基本上可以通过设置布尔标志来实现相同的目的。 continueRunning = false;while(continueRunning); 嗨,您如何在从 Thread 扩展的自定义线程中实现这一点?可以举一些例子吗? 有人能解释一下这个比较 Thread.currentThread() == runner 的目的吗? 答案中有很好的提示,但是“在哪里停止/销毁 Android Service 类中的线程?”这个问题的答案在哪里?我看不到它:)。 这是因为实际问题是:“有谁知道在终止应用程序之前终止所有线程的最佳方法是什么?”这就是我的回答。至于在 Service 中停止线程的位置,答案取决于您,但一般天真的答案是当您在线程上没有任何工作要做时。【参考方案2】:

其实你不需要上面描述的“runner”变量,比如:

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

但一般来说,坐在 Thread.sleep() 循环中是一个非常糟糕的主意。

查看新的 1.5 API 中的 AsyncTask API。它可能会比使用服务更优雅地解决您的问题。您的手机变慢了,因为服务永远不会关闭 - 没有什么会导致服务自行终止。

【讨论】:

以上是关于在哪里停止/销毁 Android Service 类中的线程?的主要内容,如果未能解决你的问题,请参考以下文章

关于Android服务

Android Service

Android Service

Android Service详解

android service 自动解绑并销毁

Android如何启动service