Android - лучший и безопасный способ остановить поток


79

Я хочу знать, как лучше всего остановить поток в Android. Я знаю, что могу использовать AsyncTaskвместо него и что есть cancel()метод. Я должен использовать Threads в моей ситуации. Вот как я использую Thread:

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

Итак, кто-нибудь знает, как лучше всего остановить поток?

Ответы:


90

Вы должны сделать так, чтобы ваши потоки поддерживали прерывания. По сути, вы можете вызвать, yourThread.interrupt()чтобы остановить поток, и в вашем методе run () вам нужно будет периодически проверять статусThread.interrupted()

Существует хороший учебник здесь .


Спасибо, я все время искал отмену () или остановку ().
Милош Черниловский

stop()Метод @ MilošČernilovský устарел, и вы больше не должны его использовать.
Alston

29

Эта ситуация ничем не отличается от стандартной Java. Вы можете использовать стандартный способ остановить поток:

class WorkerThread extends Thread {
    volatile boolean running = true;

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

Основная идея - время от времени проверять значение поля. Когда вам нужно остановить поток, вы устанавливаете runningзначение false. Кроме того, как указал Крис, вы можете использовать механизм прерывания.

Кстати, когда вы используете AsyncTask, ваш аппорач не будет сильно отличаться. Единственное отличие состоит в том, что вам придется вызывать isCancel()метод из вашей задачи вместо того, чтобы иметь специальное поле. Если вы вызовете cancel(true), но не реализуете этот механизм, поток все равно не остановится сам по себе, он будет работать до конца.


23

На Android действуют те же правила, что и в обычной среде Java. В Java потоки не уничтожаются, но остановка потока выполняется совместно . Требуется завершить поток, после чего он может корректно завершить работу.

Часто используется volatile booleanполе, которое поток периодически проверяет и завершает работу, когда для него установлено соответствующее значение.

Я бы не стал использовать a, booleanчтобы проверять, следует ли завершать поток . Если вы используете volatileв качестве модификатора поля, это будет работать надежно, но если ваш код станет более сложным, поскольку вместо этого используются другие методы блокировки внутри whileцикла, может случиться так, что ваш код вообще не завершится или, по крайней мере, займет больше времени, чем вы может захотеть.

Некоторые методы библиотеки блокировки поддерживают прерывание.

Каждый поток уже имеет состояние прерывания логического флага, и вы должны его использовать. Реализовать это можно так:

public void run() {

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

}

public void cancel() { interrupt(); }

Исходный код взят из Java Concurrency in Practice . Посколькуcancel() метод является общедоступным, вы можете позволить другому потоку вызывать этот метод по своему усмотрению.

Существует также статический метод с плохим именем, interruptedкоторый очищает прерванный статус текущего потока.


2
В Android Studio говорится, что Exception 'java.lang.InterruptedException' никогда не создается в соответствующем блоке try?
CraPo

1
@CraPo Добавьте это перед циклом while:if (Thread.interrupted()) { throw new InterruptedException();}
Kosso

16

Thread.stop()Метод , который может быть использован , чтобы остановить поток устарел; для получения дополнительной информации см; Почему Thread.stop, Thread.suspend и Thread.resume устарели? .

Лучше всего иметь переменную, с которой сам поток обращается к нему и добровольно завершает работу, если переменная равна определенному значению. Затем вы манипулируете переменной внутри своего кода, когда хотите, чтобы поток завершился. В качестве альтернативы, конечно, вы можете использовать AsyncTaskвместо.


4
AsyncTask на самом деле не альтернатива механизму прерывания, это скорее инструмент для взаимодействия с пользовательским интерфейсом. Как я указал в своем ответе, вам все равно придется реализовать тот же механизм в AsyncTask, что и в любом общем потоке, если вы хотите иметь возможность останавливать его во время выполнения.
Малькольм

2

В настоящее время, к сожалению, мы не можем ничего сделать, чтобы остановить обсуждение ....

Добавляя что-то к ответу Мэтта, мы можем вызвать, interrupt()но это не останавливает поток ... Просто говорит системе остановить поток, когда система хочет убить некоторые потоки. Остальное делает система, и мы можем проверить это, позвонив interrupted().

[ps: Если вы действительно согласны, interrupt()я бы попросил вас поэкспериментировать с коротким сном после звонка interrupt()]


2

Попробуйте как это

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

1

Есть два предпочтительных способа остановить поток.

  1. Создайте изменчивую логическую переменную, измените ее значение на false и проверьте внутри потока.

    volatile isRunning = false;
    
    public void run() {
    if(!isRunning) {return;}       
    
    }
    
  2. Или вы можете использовать метод interrupt (), который можно получить внутри потока.

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


0

У меня это сработало вот так. Внесите статическую переменную в основное действие и регулярно проверяйте ее, как я это сделал, как показано ниже.

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;



       }
   }

}

0

Если в вашем проекте есть класс потока с обработчиком, когда вы начали с одного из классов фрагментов, если вы хотели остановиться, вот решение, как остановить и избежать сбоя приложения при удалении фрагмента из стека.

Этот код находится в Котлине . Он отлично работает.

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



    }
}

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


0

Дело в том, что нужно проверить, запущен поток или нет !?

Поле:

private boolean runningThread = false;

В ветке:

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

Если вы хотите остановить поток, вы должны заполнить поле ниже

private boolean runningThread = false;

2
Это не остановит Thread, а только перестанет yourWork();вызываться. Чтобы проверить это, вы можете добавить Logнижеpublic void run() {
KRK

@KRK Я знаю это! и я опубликовал этот ответ, потому что он очень полезен, когда вы просто хотите что-то остановить и запустить снова, ничего не делая в потоке.
Hadi Note

@HadiNote KRK верен. Вы заявляете в своем ответе If you want to stop the thread you should make the below field: private boolean runningThread = false;. Это не так.
pookie

0

Мое требование немного отличалось от вопроса, но это также полезный способ остановить поток для выполнения своих задач. Все, что я хотел сделать, это остановить поток при выходе из экрана и возобновить его при возврате на экран.

Согласно документации Android, это будет предложенная замена для метода остановки, который устарел из API 15.

Многие варианты использования stop следует заменить кодом, который просто изменяет некоторую переменную, чтобы указать, что целевой поток должен прекратить работу. Целевой поток должен регулярно проверять эту переменную и упорядоченно возвращаться из своего метода выполнения, если переменная указывает, что он должен прекратить выполнение.

Мой класс темы

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

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

OnStop и OnResume будут выглядеть так

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

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

0

Поскольку мы знаем, что Thread.stop () устарел в JAVA, под капотом Thread.stop вызывает метод interrupt () в потоке, чтобы остановить его, прерывание должно вызываться из методов, которые заставляют поток ждать какой-то другой поток для уведомления после завершения выполнения. Прерывание ничего не вызовет для потока, если оно не будет обработано при выполнении потока, например, if(Thread.interrupted())return; Итак, в целом нам нужно в основном управлять запуском и остановкой потока, например, вызов start()метода, например, Thread.start()запускает while(true)внутри run()метода поток и проверяет состояние прерывания на каждой итерации и возвращается из потока.

Обратите внимание, что поток не умирает в следующих ситуациях:

  1. Поток еще не вернулся из run ().
  2. Доступен любой из объектов, принадлежащих потоку. (Это намекает на обнуление / удаление ссылок для GC, чтобы сделать остальное)

-2

Внутри любого класса Activity вы создаете метод, который присваивает NULL экземпляру потока, который можно использовать в качестве альтернативы устаревшему методу stop () для остановки выполнения потока:

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

У меня это работает без проблем.


11
Установка Threadпеременной в значение не nullприведет к остановке потока. Ставить аргумент nullвнутрь stopThread- пустая трата времени.
Тед Хопп

2
Как это может остановить поток? Почему где есть положительные голоса по этому поводу?
mvd 02

1
Это не останавливает поток. Поток все еще работает, и это присваивает ноль экземпляру потока, что делает невозможным управление этим потоком, потому что вы потеряли экземпляр (который будет работать до завершения процесса)
Teocci
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.