Android-停止线程的最佳安全方法

我想知道在 Android 中停止一个线程的最好方法是什么。我知道我可以使用 AsyncTask代替它,并且有一个 cancel()方法。在我的情况下,我不得不使用 Thread。下面是我使用 Thread的方法:

Runnable runnable = new Runnable() {
@Override
public void run() {
//doing some work
}
};
new Thread(runnable).start();

那么,有没有人知道停止一个线程的最好方法是什么?

205737 次浏览

The Thread.stop() method that could be used to stop a thread has been deprecated; for more info see; Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.

Your best bet is to have a variable which the thread itself consults, and voluntarily exits if the variable equals a certain value. You then manipulate the variable inside your code when you want the thread to exit. Alternately of course, you can use an AsyncTask instead.

You should make your thread support interrupts. Basically, you can call yourThread.interrupt() to stop the thread and, in your run() method you'd need to periodically check the status of Thread.interrupted()

There is a good tutorial here.

This situation isn't in any way different from the standard Java. You can use the standard way to stop a thread:

class WorkerThread extends Thread {
volatile boolean running = true;


public void run() {
// Do work...
if (!running) return;
//Continue doing the work
}
}

The main idea is to check the value of the field from time to time. When you need to stop your thread, you set running to false. Also, as Chris has pointed out, you can use the interruption mechanism.

By the way, when you use AsyncTask, your apporach won't differ much. The only difference is that you will have to call isCancel() method from your task instead of having a special field. If you call cancel(true), but don't implement this mechanism, the thread still won't stop by itself, it will run to the end.

Currently and unfortunately we can't do anything to stop the thread....

Adding something to Matt's answer we can call interrupt() but that doesn't stop thread... Just tells the system to stop the thread when system wants to kill some threads. Rest is done by system, and we can check it by calling interrupted().

[p.s. : If you are really going with interrupt() I would ask you to do some experiments with a short sleep after calling interrupt()]

Inside of any Activity class you create a method that will assign NULL to thread instance which can be used as an alternative to the depreciated stop() method for stopping thread execution:

public class MyActivity extends Activity {


private Thread mThread;


@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);




mThread =  new Thread(){
@Override
public void run(){
// Perform thread commands...
for (int i=0; i < 5000; i++)
{
// do something...
}


// Call the stopThread() method.
stopThread(this);
}
};


// Start the thread.
mThread.start();
}


private synchronized void stopThread(Thread theThread)
{
if (theThread != null)
{
theThread = null;
}
}
}

This works for me without a problem.

On Android the same rules apply as in a normal Java environment. In Java threads are not killed, but the stopping of a thread is done in a cooperative way. The thread is asked to terminate and the thread can then shutdown gracefully.

Often a volatile boolean field is used which the thread periodically checks and terminates when it is set to the corresponding value.

I would not use a boolean to check whether the thread should terminate. If you use volatile as a field modifier, this will work reliable, but if your code becomes more complex, for instead uses other blocking methods inside the while loop, it might happen, that your code will not terminate at all or at least takes longer as you might want.

Certain blocking library methods support interruption.

Every thread has already a boolean flag interrupted status and you should make use of it. It can be implemented like this:

public void run() {


try {
while(!Thread.currentThread().isInterrupted()) {
// ...
}
} catch (InterruptedException consumed)
/* Allow thread to exit */
}


}


public void cancel() { interrupt(); }

Source code taken from Java Concurrency in Practice. Since the cancel() method is public you can let another thread invoke this method as you wanted.

There is also a poorly named static method interrupted which clears the interrupted status of the current thread.

The thing is you need to check whether the thread is running or not !?

Field:

private boolean runningThread = false;

In the thread:

new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep((long) Math.floor(speed));
if (!runningThread) {
return;
}
yourWork();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();

If you want to stop the thread you should make the below field

private boolean runningThread = false;

There are 2 following ways preferred to stop a thread.

  1. Create a volatile boolean variable and change its value to false and check inside the thread.

    volatile isRunning = false;
    
    
    public void run() {
    if(!isRunning) {return;}
    
    
    }
    
  2. Or you can use the interrupt() method which can be receive inside a thread.

    SomeThread.interrupt();
    
    
    public void run() {
    if(Thread.currentThread.isInterrupted()) {return;}
    }
    

I used this method.

Looper.myLooper().quit();

you can try.

This worked for me like this. Introduce a static variable in main activity and regularly check for it how i did was below.

public class MainActivity extends AppCompatActivity {




//This is the static variable introduced in main activity
public static boolean stopThread =false;






protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


Thread thread = new Thread(new Thread1());
thread.start();


Button stp_thread= findViewById(R.id.button_stop);


stp_thread.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v){
stopThread = true;
}


}


}




class Thread1 implements Runnable{


public void run() {


// YOU CAN DO IT ON BELOW WAY
while(!MainActivity.stopThread) {
Do Something here
}




//OR YOU CAN CALL RETURN AFTER EVERY LINE LIKE BELOW


process 1 goes here;


//Below method also could be used
if(stopThread==true){
return ;
}
// use this after every line






process 2 goes here;




//Below method also could be used
if(stopThread==true){
return ;
}
// use this after every line




process 3 goes here;




//Below method also could be used
if(stopThread==true){
return ;
}
// use this after every line




process 4 goes here;






}
}


}

If there is thread class with a handler in your project, when you started from one of the fragment class if you wanted to stop here is the solution how to stop and avoid crashing the app when fragment removes from the stack.

This code is in Kotlin. It perfectly works.

class NewsFragment : Fragment() {


private var mGetRSSFeedsThread: GetRSSFeedsThread? = null


private val mHandler = object : Handler() {


override fun handleMessage(msg: Message?) {




if (msg?.what == GetRSSFeedsThread.GETRSSFEEDSTHREAD_SUCCESS) {
val updateXMLdata = msg.obj as String
if (!updateXMLdata.isNullOrEmpty())
parseUpdatePager(CommonUtils.getJSONObjectFromXML(updateXMLdata).toString())


} else if (msg?.what == GetRSSFeedsThread.GETRSSFEEDSTHREAD_SUCCESS) {
BaseActivity.make_toast(activity, resources.getString(R.string.pleaseTryAgain))
}


}
}
private var rootview: View? = null;
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
rootview = inflater?.inflate(R.layout.fragment_news, container, false);


news_listView = rootview?.findViewById(R.id.news_listView)
mGetRSSFeedsThread = GetRSSFeedsThread(this.activity, mHandler)




if (CommonUtils.isInternetAvailable(activity)) {
mGetRSSFeedsThread?.start()
}




return rootview
}


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true);


}






override fun onAttach(context: Context?) {
super.onAttach(context)
println("onAttach")
}


override fun onPause() {
super.onPause()
println("onPause fragment may return to active state again")


Thread.interrupted()


}


override fun onStart() {
super.onStart()
println("onStart")


}


override fun onResume() {
super.onResume()
println("onResume fragment may return to active state again")


}


override fun onDetach() {
super.onDetach()
println("onDetach fragment never return to active state again")


}


override fun onDestroy() {
super.onDestroy()


println("onDestroy fragment never return to active state again")
//check the state of the task
if (mGetRSSFeedsThread != null && mGetRSSFeedsThread?.isAlive!!) {
mGetRSSFeedsThread?.interrupt();
} else {


}


}


override fun onDestroyView() {
super.onDestroyView()
println("onDestroyView fragment may return to active state again")






}


override fun onStop() {
super.onStop()
println("onStop fragment may return to active state again")






}
}

Above code stops the running thread when you switch to any other fragment or activity from current fragment. also it recreates when you return to current fragment

Try Like this

Thread thread = new Thread() {
@Override
public void run() {
Looper.prepare();
while(true){
Log.d("Current Thread", "Running");
try{
Thread.sleep(1000);
}catch(Exeption exception){ }
}
}
};


thread.start();
thread.interrupt();

My requirement was slightly different than the question, still this is also a useful way of stopping the thread to be executing its tasks. All I wanted to do is to stop the thread on exiting the screen and resumes while returning to the screen.

As per the Android docs, this would be the proposed replacement for stop method which has been deprecated from API 15

Many uses of stop should be replaced by code that simply modifies some variable to indicate that the target thread should stop running. The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running.

My Thread class

   class ThreadClass implements Runnable {
...
@Override
public void run() {
while (count < name.length()) {


if (!exited) // checks boolean
{
// perform your task
}
...

OnStop and OnResume would look like this

  @Override
protected void onStop() {
super.onStop();
exited = true;
}


@Override
protected void onResume() {
super.onResume();
exited = false;
}

As we know that the Thread.stop() is deprecated in JAVA, under the hood the Thread.stop calls the interrupt() method on the thread to stop it, Interrupt is meant to be thrown from the methods which keep the thread waiting for some other thread to notify after the execution completes. Interrupt will cause nothing to the thread if it is not handled in the execution of a thread, like, if(Thread.interrupted())return; So, all in all we need to basically manage the start and stop of the thread like calling the start() method like Thread.start() starts a while(true) inside the run() method of the thread and checks for interrupted status in each iteration and returns from the thread.

Please note that a thread will not die in the following situations:

  1. The thread has not yet returned from the run().
  2. Any of the objects owned by the thread is accessible. (This hints to null/dispose of the references for GC to do the rest)