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.