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

Існує хороший підручник тут .


Дякую, я весь час шукав відмінити () або зупинити ().
Miloš Černilovský

stop()Метод @ MilošČernilovský застарілий, і ви більше не повинні ним користуватися.
Алстон,

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поле, яке потік періодично перевіряє і завершує, коли йому встановлено відповідне значення.

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

Деякі методи блокування бібліотеки підтримують переривання.

Кожен потік вже має логічний статус перерваного стану, і ви повинні ним скористатися. Це можна реалізувати так:

public void run() {

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

}

public void cancel() { interrupt(); }

Вихідний код взято з Java Concurrency на практиці . Оскільки cancel()метод є загальнодоступним, ви можете дозволити іншому потоку викликати цей метод як завгодно.

Існує також погано названий статичний метод interrupted який очищає перерваний стан поточного потоку.


2
В Android Studio сказано, що виняток 'java.lang.InterruptedException' ніколи не передається у відповідний блок спроби?
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

Існує 2 наступні способи, які бажано зупинити.

  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 Я це знаю! і я опублікував цю відповідь, тому що це дуже корисно, коли ви просто хочете щось зупинити і розпочати знову, нічого не роблячи.
Примітка Хаді

@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 () у потоці, щоб зупинити його, Interrupt призначений для викидання з методів, які змушують потік чекати на якийсь інший потік для повідомлення після завершення виконання. Переривання нічого не призведе до потоку, якщо воно не обробляється під час виконання потоку, наприклад, if(Thread.interrupted())return; Отже, загалом нам потрібно в основному управляти запуском і зупинкою потоку, як виклик start()методу, як Thread.start()запускається while(true)всерединіrun() методу потік та перевіряє перерваний стан на кожній ітерації та повертається з потоку.

Зверніть увагу, що нитка не загине в таких ситуаціях:

  1. Потік ще не повернувся із циклу ().
  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
Встановлення a Thread змінної значення nullнічого не зупинить потік. Встановлення аргументу nullвсередині stopThread- повна втрата часу.
Тед Хопп,

2
Як це може зупинити нитку? Чому де позитивні голоси з цього приводу?
mvd

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