在哪里停止/销毁 Android 服务类中的线程? [英] Where to stop/destroy threads in Android Service class?

查看:18
本文介绍了在哪里停止/销毁 Android 服务类中的线程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have created a threaded service the following way:

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

...
}

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

After I close the application the phone works really slow and I guess it is due to thread termination failure.

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

Does anyone know what is the best way to terminate all threads before terminating the application?

推荐答案

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

Addendum: The Android framework provides many helpers for one-off work, background work, etc, which may be preferable over trying to roll your own thread in many instances. As mentioned in a below post, AsyncTask is a good starting point to look into. I encourage readers to look into the framework provisions first before even beginning to think about doing their own threading.

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

There are several problems in the code sample you posted I will address in order:

1) Thread.stop() 已经弃用了很长时间,因为在某些情况下它会使因变量处于不一致的状态.有关详细信息,请参阅此 Sun 答复页面(该链接现已失效,请参阅 这个页面为什么不使用 Thread.stop()).停止和启动线程的首选方法如下(假设您的线程将无限期地运行):

1) Thread.stop() has been deprecated for quite some time now, as it can leave dependent variables in inconsistent states in some circumstances. See this Sun answer page for more details ( that link is now dead, see this page for why not to use Thread.stop()). A preferred method of stopping and starting a thread is as follows (assuming your thread will run somewhat indefinitely):

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 变量,也可以通过互斥锁等),并在您的线程逻辑中,使用该通信方法来检查您是否应该提前退出、清理等.

This is just one example of how to stop a thread, but the takeaway is that you are responsible for exiting a thread just as you would any other method. Maintain a method of cross thread communcation (in this case a volatile variable, could also be through a mutex, etc) and within your thread logic, use that method of communication to check if you should early exit, cleanup, etc.

2) 您的测量列表由多个线程(事件线程和您的用户线程)同时访问,没有任何同步.看起来您不必滚动自己的同步,您可以使用 阻塞队列.

2) Your measurements list is accessed by multiple threads (the event thread and your user thread) at the same time without any synchronization. It looks like you don't have to roll your own synchronization, you can use a BlockingQueue.

3) 您每次发送线程的迭代都会创建一个新的 Socket.这是一个相当重量级的操作,只有在您预计测量非常少(比如一小时或更短)时才真正有意义.要么您想要一个不会在线程的每个循环中都重新创建的持久套接字,要么您想要一个一次性运行的套接字,您可以触发并忘记"它创建一个套接字,发送所有相关数据并完成.(使用持久化Socket的快速说明,阻塞的socket方法,比如读取,不能被Thread.interrupt()中断,所以当你想停止线程时,你必须关闭socket并调用interrupt)

3) You are creating a new Socket every iteration of your sending Thread. This is a rather heavyweight operation, and only really make sense if you expect measurements to be extremely infrequent (say one an hour or less). Either you want a persistent socket that is not recreated every loop of the thread, or you want a one shot runnable you can 'fire and forget' which creates a socket, sends all relevant data, and finishes. (A quick note about using a persistent Socket, socket methods which block, such as reading, cannot be interrupted by Thread.interrupt(), and so when you want to stop the thread, you must close the socket as well as calling interrupt)

4) 除非您希望在其他地方捕获异常,否则从线程内抛出您自己的异常没有什么意义.更好的解决方案是记录错误,如果无法恢复,则停止线程.一个线程可以用类似的代码停止自己(在与上面相同的上下文中):

4) There is little point in throwing your own exceptions from within a Thread unless you expect to catch it somewhere else. A better solution is to log the error and if it is irrecoverable, stop the thread. A thread can stop itself with code like (in the same context as above):

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 将确保它会自动销毁.

Finally, if you want to be sure a thread exits with the rest of your application, no matter what, a good technique is to call Thread.setDaemon(true) after creation and before you start the thread. This flags the thread as a daemon thread, meaning the VM will ensure that it is automatically destroyed if there are no non-daemon threads running (such as if your app quits).

遵守关于线程的最佳实践应该确保您的应用不会挂起或降低手机速度,尽管它们可能非常复杂:)

Obeying best practices with regards to Threads should ensure that your app doesn't hang or slow down the phone, though they can be quite complex :)

这篇关于在哪里停止/销毁 Android 服务类中的线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆