segundo procesos plano cancel asynctask java android multithreading android-asynctask runnable

procesos - handler android java



¿Cómo usar Runnable.wait() en AsyncTask? ¿Por qué la AsyncTask NO espera...? (1)

Los basicos

Cuando inicia una AsyncTask primero el método onPreExecute() se ejecuta en el subproceso de la interfaz de usuario. Puede anular este método para realizar cambios en la interfaz de usuario antes de ejecutar el método doInBackground() .

Después de que el método doInBackground() finaliza, el método onPostExecute() se ejecuta en el subproceso de la interfaz de usuario, por lo que puede usarlo para realizar cambios en la interfaz de usuario desde aquí. Si necesita realizar cambios regulares en el subproceso UI durante el método doInBackground() , anula el método onProgressUpdate() que se ejecuta en el subproceso UI, y luego lo llama desde doInBackground() , lo que le permitirá actualizar periódicamente la interfaz de usuario .

Podría usar algo como lo siguiente;

private class DoStuffTask extends AsyncTask { @Override protected void doInBackground(Object... args) { // Do stuff onProgressUpdate(x); // Do more stuff } @Override protected void onProgressUpdate(Object... args) { // Update your UI here } }

Ahora bien, si esto no funciona y quiere que AsyncTask espere la entrada durante el método doInBackground() , probablemente valga la pena considerar el uso de varias AsyncTasks . Luego puede terminar cada AsyncTask , solicitar una entrada y luego iniciar una nueva AsyncTask para continuar trabajando.

Dado que AlertDialog instancias de AlertDialog son asíncronas, esta es probablemente la solución preferida porque puede iniciar la próxima AsyncTask desde AlertDialog .

Usando wait () en un AsyncTask

Si prefiere usar una sola AsyncTask , puede usar wait desde su AsyncTask para evitar que la ejecución continúe hasta que se cumpla alguna condición. En lugar de usar un Runnable nuevo, Runnable estamos usando dos hilos en este caso, el hilo que ejecuta doInBackground() y el hilo principal, y estamos sincronizando en el AsycTask mismo.

Ejemplo a continuación;

public class TestTask extends AsyncTask{ private boolean notified; private Promptable p; public interface Promptable { public abstract void prompt(); } public TestTask(Promptable p){ this.p = p; } @Override protected Object doInBackground(Object... arg0) { Log.d("First", "First"); onProgressUpdate(null); synchronized(this){ while(!notified){ try{ this.wait(); } catch(InterruptedException e){ } } } Log.d("Second", "Second"); return null; } @Override protected void onProgressUpdate(Object... args){ synchronized(this){ notified = true; p.prompt(); this.notify(); } } }

En el ejemplo anterior, suponga que su Activity se analiza en el AsyncTask la AsyncTask , y que implementa una interfaz que creamos llamada Promptable . Notarás que aunque estamos llamando a wait() lo estamos colocando en un ciclo while. Si no hicimos esto, y de alguna manera notify() antes de wait() , el hilo se bloqueará indefinidamente. Además, no puede depender del hecho de que su hilo esperará para siempre, por lo que el ciclo while asegura que no continuará hasta que se llame a notify.

Espero que esto ayude.

Estoy usando AsyncTask para ejecutar una operación en segundo plano. Por supuesto, cambiar a otro hilo mientras se trabaja en un hilo de fondo no tiene mucho sentido en general, excepto que el otro hilo es el hilo de UI. Esto es lo que me gustaría hacer: mientras la tarea se está ejecutando, necesito "acceder" a la interfaz de usuario, por ejemplo, para mostrar un cuadro de diálogo y preguntarle al usuario cómo proceder.

  • ejecutar la tarea de fondo
  • detenga la tarea en algún momento para obtener comentarios de los usuarios
  • cambiar a la secuencia de la interfaz de usuario para mostrar el cuadro de diálogo y solicitar la entrada
  • volver a la tarea de fondo y continuar el trabajo

¿Cómo puede hacerse esto? Pensé que podría usar Runnable con myActivity.runOnUiThread(runnable) pero esto no funciona:

private void copyFiles() { CopyTask copyTask = new CopyTask(this); copyTask.execute(); } // CustomAsyncTask is a AsyncTask subclass that takes a reference to the current // activity as parameter private class CopyTask extends CustomAsyncTask<Void, Void, Void> { private doCopy; @Override protected Boolean doInBackground(Void... params) { // Custom code, e.g. copy files from A to B and check for conflict for (File file : allFiles) { doCopy = true; if (isConflict(file)) { // Stop current thread and ask for user feedback on UI Thread Runnable uiRunnable = new Runnable() { public void run() { // Pos 1. --> Execute custom code, e.g. use AlertDialog to ask user if file should be replaced... doCopy = false; synchronized (this) { this.notify(); } } }); synchronized(uiRunnable) { // Execute code on UI thread activity.runOnUiThread(uiRunnable); // Wait until runnable finished try { uiRunnable.wait(); } catch (InterruptedException e ) { e.printStackTrace(); } } } // Pos 2. --> Continue work if (doCopy) copyFromAToB(File); } return null; } }

Dentro de doInBackground() (-> en una AsyncTask fondo), AsyncTask llama a activity.runOnUiThread(uiRunnable) . Se llama a uiRunnable.wait() . En cuanto a docu wait() debe hacer lo siguiente:

Hace que el hilo que llama espere hasta que otro hilo llame al método notify () o notifyAll () de este objeto.

Por lo tanto, el hilo de fondo debe esperar para continuar su trabajo hasta que se this.notify() (== uiRunnable.notifiy() ) en otro hilo (= el hilo de UI), ¿no es así?

Bueno, ¡id no espera! Después de llamar a uiRunnable.wait() el hilo de fondo continúa inmediatamente saltando a if (doCopy)... Parece que el hilo de fondo y el hilo principal se ejecutan en paralelo (lo cual no es sorprendente ya que esto es lo que hacen los hilos ...) y es una condición de carrera si doCopy = false en el hilo de UI o if (doCopy) en el hilo de fondo se alcanza primero

¿Cómo es esto posible? ¿Por qué does not wait() funciona como se describe? ¿O estoy recibiendo algo mal?

¡Muchas gracias!

EDITAR: Para evitar malentendidos: por supuesto que conozco los métodos del ciclo de vida de AsyncTask, pero por lo que yo los entiendo, no son lo que estoy buscando (ver mi respuesta al comentario).

Interrumpir AsyncTask tan pronto como sea necesaria una interacción de UI, consultar la interfaz de usuario y comenzar una nueva AsyncTask sería posible, por supuesto. Sin embargo, esto daría como resultado un código que es muy difícil de leer / comprender / mantener.

Como entiendo el documento de wait() todo debería funcionar bien aquí. La pregunta principal no es cómo hacer la interacción de UI durante el ciclo de vida de una AsyncTask sino por qué wait() no funciona como se esperaba.