Android - Der beste und sicherste Weg, um Thread zu stoppen

79

Ich möchte wissen, wie man einen Thread in Android am besten stoppt. Ich weiß, dass ich AsyncTaskstattdessen verwenden kann und dass es eine cancel()Methode gibt. Ich muss Threads in meiner Situation verwenden. Hier ist, wie ich benutze Thread:

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

Hat jemand eine Idee, wie man einen Thread am besten stoppen kann?

Android-Droid
quelle

Antworten:

88

Sie sollten dafür sorgen, dass Ihr Thread Interrupts unterstützt. Grundsätzlich können Sie aufrufen yourThread.interrupt(), um den Thread zu stoppen, und in Ihrer run () -Methode müssen Sie den Status von regelmäßig überprüfenThread.interrupted()

Es ist ein gutes Tutorial hier .

Chris
quelle
Danke, ich habe die ganze Zeit nach cancel () oder stop () gesucht.
Miloš Černilovský
@ MilošČernilovský stop()Methode ist veraltet und Sie sollten das nicht mehr verwenden.
Alston
29

Diese Situation unterscheidet sich in keiner Weise von der Standard-Java. Sie können die Standardmethode zum Stoppen eines Threads verwenden:

class WorkerThread extends Thread {
    volatile boolean running = true;

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

Die Hauptidee ist, von Zeit zu Zeit den Wert des Feldes zu überprüfen. Wenn Sie Ihren Thread stoppen müssen, setzen Sie runningauf false. Wie Chris bereits betont hat, können Sie auch den Unterbrechungsmechanismus verwenden.

Übrigens, wenn Sie AsyncTask verwenden, wird sich Ihr Ansatz nicht wesentlich unterscheiden. Der einzige Unterschied besteht darin, dass Sie die isCancel()Methode Ihrer Aufgabe aufrufen müssen, anstatt ein spezielles Feld zu haben. Wenn Sie cancel(true)diesen Mechanismus aufrufen , aber nicht implementieren, stoppt der Thread immer noch nicht von selbst, sondern läuft bis zum Ende.

Malcolm
quelle
23

Unter Android gelten die gleichen Regeln wie in einer normalen Java-Umgebung. In Java werden Threads nicht getötet, aber das Stoppen eines Threads erfolgt auf kooperative Weise . Der Thread wird zum Beenden aufgefordert und der Thread kann dann ordnungsgemäß heruntergefahren werden.

Oft wird ein volatile booleanFeld verwendet, das der Thread regelmäßig überprüft und beendet, wenn er auf den entsprechenden Wert gesetzt wird.

Ich würde a nicht verwenden boolean, um zu überprüfen, ob der Thread beendet werden soll . Wenn Sie volatileals Feldmodifikator verwenden, funktioniert dies zuverlässig. Wenn Ihr Code jedoch komplexer wird und stattdessen andere Blockierungsmethoden innerhalb der whileSchleife verwendet, kann es vorkommen, dass Ihr Code überhaupt nicht beendet wird oder zumindest länger dauert als Sie könnte wollen.

Bestimmte blockierende Bibliotheksmethoden unterstützen die Unterbrechung.

Jeder Thread hat bereits einen Status eines unterbrochenen Booleschen Flags, den Sie verwenden sollten. Es kann folgendermaßen implementiert werden:

public void run() {

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

}

public void cancel() { interrupt(); }

Quellcode aus Java Concurrency in der Praxis . Da die cancel()Methode öffentlich ist, können Sie einen anderen Thread diese Methode wie gewünscht aufrufen lassen.

Es gibt auch eine schlecht benannte statische Methode, interrupteddie den unterbrochenen Status des aktuellen Threads löscht.

Konrad Reiche
quelle
2
In Android Studio heißt es, dass die Ausnahme 'java.lang.InterruptedException' niemals in den entsprechenden try-Block geworfen wird?
CraPo
1
@CraPo Fügen Sie dies vor der while-Schleife hinzu:if (Thread.interrupted()) { throw new InterruptedException();}
Kosso
16

Die Thread.stop()Methode zum Stoppen eines Threads ist veraltet. Für weitere Informationen siehe; Warum sind Thread.stop, Thread.suspend und Thread.resume veraltet? .

Am besten haben Sie eine Variable, die der Thread selbst konsultiert und freiwillig beendet, wenn die Variable einem bestimmten Wert entspricht. Anschließend bearbeiten Sie die Variable in Ihrem Code, wenn der Thread beendet werden soll. Alternativ können Sie natürlich auch eine AsyncTaskverwenden.

Matt
quelle
4
AsyncTask ist keine Alternative zum Unterbrechungsmechanismus, sondern eher ein Instrument zur Interaktion mit der Benutzeroberfläche. Wie ich in meiner Antwort ausgeführt habe, müssen Sie in der AsyncTask immer noch denselben Mechanismus implementieren wie in jedem allgemeinen Thread, wenn Sie ihn während der Ausführung stoppen möchten.
Malcolm
2

Derzeit und leider können wir nichts tun, um den Thread zu stoppen ....

Fügen Sie etwas zu Matts Antwort hinzu, die wir aufrufen können, interrupt()aber das stoppt den Thread nicht ... Weist das System nur an, den Thread zu stoppen, wenn das System einige Threads beenden möchte. Der Rest wird vom System erledigt und wir können ihn durch einen Anruf überprüfen interrupted().

[ps: Wenn Sie wirklich mitmachen, interrupt()würde ich Sie bitten, nach dem Anruf einige Experimente mit einem kurzen Schlaf durchzuführen interrupt()]

Prasham
quelle
1

Es gibt zwei Möglichkeiten, einen Thread zu stoppen.

  1. Erstellen Sie eine flüchtige boolesche Variable, ändern Sie ihren Wert in false und überprüfen Sie sie im Thread.

    flüchtig isRunning = false;

    public void run () {if (! isRunning) {return;}}

  2. Oder Sie können die Interrupt () -Methode verwenden, die innerhalb eines Threads empfangen werden kann.

    SomeThread.interrupt ();

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

Suresh Sharma
quelle
1

Versuchen Sie es so

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

thread.start();
thread.interrupt();
Udara Kasun
quelle
0

Ich habe diese Methode angewendet.

Looper.myLooper().quit();

Du kannst es versuchen.

Yanchao Wang
quelle
0

Das hat bei mir so funktioniert. Führen Sie eine statische Variable in die Hauptaktivität ein und überprüfen Sie regelmäßig, wie ich es unten getan habe.

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;



       }
   }

}
Seyon Seyon
quelle
0

Wenn Ihr Projekt eine Thread-Klasse mit einem Handler enthält, können Sie beim Starten von einer der Fragmentklassen, wenn Sie hier anhalten möchten, die App stoppen und einen Absturz vermeiden, wenn das Fragment vom Stapel entfernt wird.

Dieser Code ist in Kotlin . Es funktioniert perfekt.

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



    }
}

Der obige Code stoppt den laufenden Thread, wenn Sie vom aktuellen Fragment zu einem anderen Fragment oder einer anderen Aktivität wechseln. Außerdem wird es neu erstellt, wenn Sie zum aktuellen Fragment zurückkehren

Suresh Maidaragi
quelle
0

Die Sache ist, müssen Sie überprüfen, ob der Thread läuft oder nicht !?

Feld:

private boolean runningThread = false;

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

Wenn Sie den Thread stoppen möchten, sollten Sie das folgende Feld erstellen

private boolean runningThread = false;
Hadi Hinweis
quelle
2
Dies wird das nicht aufhalten Thread, es wird nur aufhören yourWork();, aufgerufen zu werden. Um dies zu testen, können Sie eine Logunten hinzufügenpublic void run() {
KRK
@KRK Ich weiß das! und ich habe diese Antwort gepostet, weil sie sehr nützlich ist, wenn Sie nur etwas stoppen und erneut starten möchten, ohne etwas für den Thread zu tun.
Hadi Note
@HadiNote KRK ist korrekt. Sie geben in Ihrer Antwort an If you want to stop the thread you should make the below field: private boolean runningThread = false;. Das ist nicht richtig.
Pookie
0

Meine Anforderung war etwas anders als die Frage, dennoch ist dies auch eine nützliche Möglichkeit, den Thread anzuhalten, um seine Aufgaben auszuführen. Alles, was ich tun wollte, ist, den Thread beim Verlassen des Bildschirms anzuhalten und fortzusetzen, während ich zum Bildschirm zurückkehre.

Gemäß den Android-Dokumenten wäre dies der vorgeschlagene Ersatz für die Stop- Methode, die von API 15 nicht mehr unterstützt wird

Viele Verwendungen von stop sollten durch Code ersetzt werden, der einfach eine Variable ändert, um anzuzeigen, dass der Ziel-Thread nicht mehr ausgeführt werden soll. Der Ziel-Thread sollte diese Variable regelmäßig überprüfen und ordnungsgemäß von seiner Ausführungsmethode zurückkehren, wenn die Variable angibt, dass die Ausführung beendet werden soll.

Meine Thread-Klasse

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

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

OnStop und OnResume würden so aussehen

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

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

Da wir wissen, dass Thread.stop () in JAVA veraltet ist, ruft Thread.stop unter der Haube die Interrupt () -Methode für den Thread auf, um ihn zu stoppen. Interrupt soll von den Methoden ausgelöst werden, auf die der Thread wartet Ein anderer Thread, der nach Abschluss der Ausführung benachrichtigt werden soll. Ein Interrupt verursacht nichts für den Thread, wenn er bei der Ausführung eines Threads nicht behandelt wird, wie z. B. if(Thread.interrupted())return; Alles in allem müssen wir also den Start und Stopp des Threads so verwalten, als würde die start()Methode aufgerufen , wie Thread.start()ein while(true)innerhalb der run()Methode des Threads Thread und prüft in jeder Iteration auf unterbrochenen Status und kehrt vom Thread zurück.

Bitte beachten Sie, dass ein Thread in den folgenden Situationen nicht stirbt:

  1. Der Thread ist noch nicht von run () zurückgekehrt.
  2. Auf alle Objekte des Threads kann zugegriffen werden. (Dies deutet darauf hin, dass die Referenzen für GC null / entsorgt werden müssen, um den Rest zu erledigen.)
SaadurRehman
quelle
-2

Innerhalb einer Activity-Klasse erstellen Sie eine Methode, die der Thread-Instanz NULL zuweist, die als Alternative zur veralteten stop () -Methode zum Stoppen der Thread-Ausführung verwendet werden kann:

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

Das funktioniert bei mir problemlos.

John Verco
quelle
11
Wenn Sie eine ThreadVariable auf setzen, nullwird der Thread nicht gestoppt. Das Argument nach nullinnen zu setzen stopThreadist reine Zeitverschwendung.
Ted Hopp
2
Wie kann dies möglicherweise den Thread stoppen? Warum gibt es positive Stimmen dazu?
MVD
1
Dies stoppt den Thread nicht. Der Thread läuft noch und dies weist der Instanz des Threads null zu, was es unmöglich macht, diesen Thread zu steuern, weil Sie die Instanz verloren haben (die so lange läuft, bis der Prozess beendet ist)
Teocci