Java 在哪里停止/销毁 Android 服务类中的线程?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/680180/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Where to stop/destroy threads in Android Service class?
提问by Niko Gamulin
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?
有谁知道在终止应用程序之前终止所有线程的最佳方法是什么?
采纳答案by sooniln
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.
附录:Android 框架为一次性工作、后台工作等提供了许多帮助程序,在许多情况下,这可能比尝试滚动您自己的线程更可取。如下文所述,AsyncTask 是一个很好的研究起点。我鼓励读者在开始考虑做自己的线程之前先查看框架条款。
There are several problems in the code sample you posted I will address in order:
您发布的代码示例中有几个问题,我将按顺序解决:
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 pagefor more details (Edit: 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):
1) Thread.stop() 现在已经被弃用了很长时间,因为它可以在某些情况下使因变量处于不一致的状态。有关更多详细信息,请参阅此 Sun 回答页面(编辑:该链接现已失效,请参阅此页面了解为什么不使用 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
}
}
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.
这只是如何停止线程的一个示例,但要点是您负责退出线程,就像使用任何其他方法一样。维护一种跨线程通信的方法(在这种情况下是一个 volatile 变量,也可以通过互斥锁等)并在您的线程逻辑中,使用该通信方法来检查您是否应该提前退出、清理等。
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.
2)您的测量列表由多个线程(事件线程和您的用户线程)同时访问,没有任何同步。看起来您不必滚动自己的同步,您可以使用BlockingQueue。
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)
3)您在发送线程的每次迭代中都会创建一个新的套接字。这是一个相当重量级的操作,只有当您预计测量非常少时(比如一小时或更短)才真正有意义。要么您想要一个不会在线程的每个循环中都重新创建的持久套接字,要么您想要一个一次性运行的可以“触发并忘记”的套接字,它会创建一个套接字,发送所有相关数据并完成。(使用持久化Socket的快速说明,阻塞的socket方法,比如读取,不能被Thread.interrupt()中断,所以当你想停止线程时,你必须关闭socket并调用interrupt)
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):
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
}
}
}
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).
最后,如果您想确保一个线程与应用程序的其余部分一起退出,不管怎样,一个好的技术是在创建之后和启动线程之前调用 Thread.setDaemon(true)。这将线程标记为守护线程,这意味着如果没有非守护线程在运行(例如,如果您的应用程序退出),VM 将确保它被自动销毁。
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 :)
遵守有关线程的最佳实践应确保您的应用程序不会挂起或减慢电话速度,尽管它们可能非常复杂:)
回答by sooniln
Actually, you don't need the "runner" variable as described above, something like:
实际上,您不需要如上所述的“runner”变量,例如:
while (!interrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
break;
}
}
But generally, sitting in a Thread.sleep() loop is a really bad idea.
但一般来说,坐在 Thread.sleep() 循环中是一个非常糟糕的主意。
Look at the AsyncTask API in the new 1.5 API. It will probably solve your problem more elegantly than using a service. Your phone is getting slow because the service never shuts down - there's nothing that will cause the service to kill itself.
查看新的 1.5 API 中的 AsyncTask API。它可能会比使用服务更优雅地解决您的问题。您的手机变慢了,因为服务永远不会关闭 - 没有什么会导致服务自行终止。