Android: la mejor y más segura forma de detener el hilo

79

Quiero saber cuál es la mejor manera de detener un hilo en Android. Sé que puedo usarlo AsyncTasken su lugar y que hay un cancel()método. Tengo que usar Threads en mi situación. Así es como lo estoy usando Thread:

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

Entonces, ¿alguien tiene alguna idea de cuál es la mejor manera de detener un hilo?

Android-Droid
fuente

Respuestas:

90

Debes hacer que el soporte de tu hilo se interrumpa. Básicamente, puede llamar yourThread.interrupt()para detener el hilo y, en su método run (), necesitará verificar periódicamente el estado deThread.interrupted()

Hay un buen tutorial aquí .

Chris
fuente
Gracias, estaba buscando cancelar () o detener () todo el tiempo.
Miloš Černilovský
El stop()método @ MilošČernilovský está obsoleto y ya no debería usarlo.
Alston
29

Esta situación no es diferente de la del estándar Java. Puede utilizar la forma estándar para detener un hilo:

class WorkerThread extends Thread {
    volatile boolean running = true;

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

La idea principal es comprobar el valor del campo de vez en cuando. Cuando necesita detener su hilo, lo establece runningen falso. Además, como ha señalado Chris, puede utilizar el mecanismo de interrupción.

Por cierto, cuando usa AsyncTask, su apporach no diferirá mucho. La única diferencia es que tendrás que llamar al isCancel()método desde tu tarea en lugar de tener un campo especial. Si llama cancel(true), pero no implementa este mecanismo, el hilo aún no se detendrá por sí solo, se ejecutará hasta el final.

Malcolm
fuente
23

En Android se aplican las mismas reglas que en un entorno Java normal. En Java, los hilos no se matan, pero la detención de un hilo se realiza de forma cooperativa . Se le pide al hilo que termine y el hilo puede luego cerrarse con gracia.

A menudo volatile boolean, se usa un campo que el hilo verifica periódicamente y termina cuando se establece en el valor correspondiente.

Yo no me utilizar una booleanpara comprobar si el hilo debe terminar . Si lo usa volatilecomo un modificador de campo, esto funcionará de manera confiable, pero si su código se vuelve más complejo, ya que en su lugar usa otros métodos de bloqueo dentro del whileciclo, podría suceder que su código no termine en absoluto o al menos demore más cuando podria querer.

Ciertos métodos de biblioteca de bloqueo admiten la interrupción.

Cada hilo ya tiene un estado de interrupción de bandera booleana y debe hacer uso de él. Se puede implementar así:

public void run() {

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

}

public void cancel() { interrupt(); }

Código fuente tomado de Java Concurrency in Practice . Dado que el cancel()método es público, puede permitir que otro hilo invoque este método como desee.

También hay un método estático mal nombrado interruptedque borra el estado interrumpido del hilo actual.

Konrad Reiche
fuente
2
En Android Studio dice: ¿La excepción 'java.lang.InterruptedException' nunca se lanza en el bloque try correspondiente?
CraPo
1
@CraPo Agregue esto antes del ciclo while:if (Thread.interrupted()) { throw new InterruptedException();}
Kosso
16

El Thread.stop()método que podría usarse para detener un hilo ha quedado obsoleto; para más información ver; ¿Por qué Thread.stop, Thread.suspend y Thread.resume están obsoletos? .

Su mejor opción es tener una variable que el hilo mismo consulte y salga voluntariamente si la variable es igual a un cierto valor. Luego manipula la variable dentro de su código cuando desea que el hilo salga. Alternativamente, por supuesto, puede usar un AsyncTask.

Mate
fuente
4
AsyncTask no es realmente una alternativa al mecanismo de interrupción, es más un instrumento para interactuar con la interfaz de usuario. Como señalé en mi respuesta, aún tendrá que implementar el mismo mecanismo en AsyncTask que en cualquier hilo común, si desea poder detenerlo durante la ejecución.
Malcolm
2

Actualmente y lamentablemente no podemos hacer nada para detener el hilo ...

Al agregar algo a la respuesta de Matt, podemos llamar, interrupt()pero eso no detiene el hilo ... Solo le dice al sistema que detenga el hilo cuando el sistema quiera eliminar algunos hilos. El reposo lo realiza el sistema, y ​​podemos comprobarlo llamando interrupted().

[pd: si realmente vas con, interrupt()te pediría que hagas algunos experimentos con un poco de sueño después de llamar interrupt()]

Prasham
fuente
2

Prueba así

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();
Udara Kasun
fuente
1

Hay 2 formas siguientes preferidas para detener un hilo.

  1. Cree una variable booleana volátil y cambie su valor a falso y verifique dentro del hilo.

    volatile isRunning = false;
    
    public void run() {
    if(!isRunning) {return;}       
    
    }
    
  2. O puede usar el método interrupt () que se puede recibir dentro de un hilo.

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

Usé este método.

Looper.myLooper().quit();

puedes probar.

Yanchao Wang
fuente
0

Esto funcionó para mí así. Introduzca una variable estática en la actividad principal y compruebe regularmente cómo lo hice a continuación.

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
fuente
0

Si hay una clase de subproceso con un controlador en su proyecto, cuando comenzó desde una de la clase de fragmento, si quería detenerse, aquí está la solución de cómo detener y evitar bloquear la aplicación cuando el fragmento se elimina de la pila.

Este código está en Kotlin . Funciona perfectamente.

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



    }
}

El código anterior detiene el hilo en ejecución cuando cambia a cualquier otro fragmento o actividad del fragmento actual. también se recrea cuando regresa al fragmento actual

Suresh Maidaragi
fuente
0

¡¿Lo que necesitas es comprobar si el hilo se está ejecutando o no ?!

Campo:

private boolean runningThread = false;

En el hilo:

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

Si desea detener el hilo, debe hacer el siguiente campo

private boolean runningThread = false;
Nota de Hadi
fuente
2
Esto no detendrá el Thread, solo dejará yourWork();de ser llamado. Para probar esto, puede agregar uno a Logcontinuaciónpublic void run() {
KRK
@KRK ¡Lo sé! y publiqué esta respuesta porque es muy útil cuando solo quieres detener algo y comenzar de nuevo sin hacer nada en el hilo.
Hadi Note
@HadiNote KRK es correcto. Declaras en tu respuesta If you want to stop the thread you should make the below field: private boolean runningThread = false;. Esto no es correcto.
pookie
0

Mi requisito era ligeramente diferente a la pregunta, pero también es una forma útil de detener el hilo para ejecutar sus tareas. Todo lo que quería hacer era detener el hilo al salir de la pantalla y reanudarlo mientras volvía a la pantalla.

Según los documentos de Android, este sería el reemplazo propuesto para el método de detención que ha quedado obsoleto de la API 15

Muchos usos de stop deberían ser reemplazados por código que simplemente modifica alguna variable para indicar que el hilo de destino debería dejar de ejecutarse. El hilo de destino debe verificar esta variable con regularidad y regresar de su método de ejecución de manera ordenada si la variable indica que debe dejar de ejecutarse.

Mi clase de hilo

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

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

OnStop y OnResume se verían así

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

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

Como sabemos que Thread.stop () está en desuso en JAVA, bajo el capó Thread.stop llama al método interrupt () en el hilo para detenerlo, Interrupt está destinado a ser lanzado desde los métodos que mantienen el hilo esperando algún otro hilo para notificar después de que se complete la ejecución. La interrupción no causará nada al hilo si no se maneja en la ejecución de un hilo, como, if(Thread.interrupted())return; Entonces, en general, básicamente necesitamos administrar el inicio y la parada del hilo como llamar al start()método como Thread.start()comienza while(true)dentro del run()método del hilo y comprueba el estado interrumpido en cada iteración y regresa del hilo.

Tenga en cuenta que un hilo no morirá en las siguientes situaciones:

  1. El hilo aún no ha regresado de run ().
  2. Cualquiera de los objetos propiedad del hilo es accesible. (Esto sugiere anular / eliminar las referencias para que GC haga el resto)
SaadurRehman
fuente
-2

Dentro de cualquier clase de actividad, crea un método que asignará NULL a la instancia del hilo que se puede usar como una alternativa al método depreciado stop () para detener la ejecución del hilo:

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

Esto funciona para mí sin ningún problema.

John Verco
fuente
11
Establecer una Threadvariable en nullno hará nada para detener el hilo. Poner el argumento en el nullinterior stopThreades una completa pérdida de tiempo.
Ted Hopp
2
¿Cómo puede esto detener el hilo? ¿Por qué hay votos positivos sobre esto?
mvd
1
Esto no detiene el hilo. El hilo aún se está ejecutando y esto asigna un valor nulo a la instancia del hilo, lo que hace imposible controlar ese hilo, porque perdió la instancia (que seguirá ejecutándose hasta que
finalice