2011-04-01 12 views
23

Uso una AsyncTask para realizar un proceso largo.publishProgress desde dentro de una función en doInBackground?

No quiero colocar mi código de proceso largo directamente dentro doInBackground. En cambio, mi código de proceso largo está ubicado en otra clase, a la que llamo doInBackground.

Me gustaría poder llamar a publishProgress desde dentro de la función longProcess. En C++, pasaría un puntero de devolución de llamada a publishProgress a mi función longProcess.

¿Cómo hago eso en Java?

EDIT:

Mi larga código de proceso:

public class MyLongProcessClass 
    { 
    public static void mylongProcess(File filetoRead) 
     { 
     // some code... 
     // here I would like to call publishProgress 
     // some code... 
     } 
    } 

Mi código AsyncTask:

private class ReadFileTask extends AsyncTask<File, Void, Boolean> 
    { 
    ProgressDialog taskProgress; 

    @Override 
    protected Boolean doInBackground(File... configFile) 
     { 
     MyLongProcessClass.mylongProcess(configFile[0]); 
     return true; 
     } 
    } 

editar # 2 El método largo proceso también podría ser no estático y llamado así esto:

MyLongProcessClass fileReader = new MyLongProcessClass(); 
fileReader.mylongProcess(configFile[0]); 

Pero eso no cambia mi problema.

Respuesta

32

La dificultad es que es publishProgressprotected final por lo que incluso si se pasa this en su método static llamar todavía no se puede llamar directamente publishProgress.

yo no he probado esto por mí mismo, pero ¿qué tal:

public class LongOperation extends AsyncTask<String, Integer, String> { 
    ... 

    @Override 
    protected String doInBackground(String... params) { 
     SomeClass.doStuff(this); 
     return null; 
    } 
    ... 

    public void doProgress(int value){ 
     publishProgress(value); 
    } 
} 
... 
public class SomeClass { 
    public static void doStuff(LongOperation task){ 
     // do stuff 
     task.doProgress(1); 
     // more stuff etc 
    } 
} 

si esto funciona por favor hágamelo saber! Tenga en cuenta que llamar al doProgress desde cualquier lugar que no sea un método invocado desde doInBackground causará un error.

Se siente muy sucio para mí, ¿alguien más tiene una manera mejor?

+0

podría funcionar si mi clase LongOperation no llamara a xmlReader.parse (donde se encuentra el código que consume mucho tiempo). Parece que voy a tener un simple diálogo de progreso rotativo. –

+0

Esa parece ser una buena solución. ¡Pulgares hacia arriba! – Wroclai

+0

@Regis St-Gelais si está tratando de controlar el progreso del análisis, puede probar [este enfoque] (http://stackoverflow.com/questions/5015856/android-sax-parser-progress-monitoring) o [este uno] (http://stackoverflow.com/questions/3100013/java-sax-parser-progress-monitoring) –

0

Cuando dice "mi código largo proceso se encuentra en otra clase que yo llamo en doInBackground", Qué quiere decir "situado en otro método que llamo en doInBackground"?

Si es así, podría hacer que ese método sea un método privado de su clase AsynTask. Luego, puede llamar a publishProgress dentro del método cuando sea necesario.

+0

No. Se trata de un método estático ubicado en una clase estática que se encuentra fuera de la clase AsyncTask –

+0

No he podido editar la pregunta para aclarar –

2

Divida la función longProcess() en funciones más pequeñas.

Código de ejemplo:

@Override 
protected Boolean doInBackground(Void... params) { 
    YourClass.yourStaticMethodOne(); 
    publishProgress(1); 
    YourClass.yourStaticMethodTwo(); 
    publishProgress(2); 
    YourClass.yourStaticMethodThree(); 
    publishProgress(3); 

    // And so on... 
    return true; 
} 
+0

(o para que se haya presentado una queja). Es un analizador xml. –

+0

@Regis St-Gelais: ¿Qué es complicado? Si obtiene un valor de uno de estos métodos estáticos, simplemente puede alcanzarlo y pasarlo a la siguiente función. AFAIK, no hay otra solución disponible. – Wroclai

+0

¿y los delegados? –

1

Si esto funciona, hágamelo saber! Tenga en cuenta que llamar a doProgress desde cualquier lugar que no sea un método invocado desde doInBackground causará un error.

Sí, funciona. Lo extendí para que no necesites pasar AsyncTask como parámetro a tu método.Esto es particularmente útil si (como yo) que ya ha escrito todas sus métodos antes de decidir que en realidad sí es necesario para publicar un cierto progreso, o en mi caso, actualizar la interfaz de usuario de un AsyncTask:

public abstract class ModifiedAsyncTask<A,B,C> extends AsyncTask<A,B,C>{ 

    private static final HashMap<Thread,ModifiedAsyncTask<?,?,?>> threads 
      = new HashMap<Thread,ModifiedAsyncTask<?,?,?>>(); 

    @Override 
    protected C doInBackground(A... params) { 
     threads.put(Thread.currentThread(), this); 
     return null;   
    } 

    public static <T> void publishProgressCustom(T... t) throws ClassCastException{ 
     ModifiedAsyncTask<?, T, ?> task = null; 
     try{ 
      task = (ModifiedAsyncTask<?, T, ?>) threads.get(Thread.currentThread()); 
     }catch(ClassCastException e){ 
      throw e; 
     } 
     if(task!=null) 
      task.publishProgress(t); 
    } 
} 

public class testThreadsActivity extends Activity { 

/** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main);   
    } 

    public void Button1Clicked(View v){ 
     MyThread mthread = new MyThread(); 
     mthread.execute((Void[])null);  
    } 

    private class MyThread extends ModifiedAsyncTask<Void, Long, Void>{ 

     @Override 
     protected Void doInBackground(Void... params) { 
      super.doInBackground(params); 

      while(true){ 
       myMethod(System.currentTimeMillis());    
       try { 
        Thread.sleep(1000L); 
       } catch (InterruptedException e) {     
        return null; 
       } 
      }   
     } 

     protected void onProgressUpdate(Long... progress) { 
      //Update UI 
      ((TextView) findViewById(R.id.textView2)).setText("The Time is:" + progress[0]); 
     } 


    } 

    private void myMethod(long l){ 

     // do something 

     // request UI update 
     ModifiedAsyncTask.publishProgressCustom(new Long[]{l}); 
    } 

} 

Me siento muy sucio, ¿alguien más tiene una mejor manera?

Mi camino es probablemente peor. Estoy llamando a un método estático para doProgress (que llamé publishProgressCustom). Se puede invocar desde cualquier lugar sin producir un error (como si el hilo no tuviera AsyncTask correspondiente en el hashMap, no llamará a publishProgress). El inconveniente es que tiene que agregar el mapeo Thread-AsyncTask usted mismo después de que el hilo ha comenzado. (No puede anular AsyncTask.execute() tristemente ya que esto es definitivo). Lo he hecho aquí anulando doInBackground() en la superclase, de modo que cualquiera que lo extienda solo tiene que poner super.doInBackground() como la primera línea en su propio doInBackground().

No sé lo suficiente sobre Threads y AsyncTask para saber qué sucede con las referencias de HashMap una vez que el Thread y/o AsyncTask llega a su fin. Sospecho que suceden cosas malas, así que no sugeriría que nadie pruebe mi solución como parte de la suya a menos que sepan mejor

+0

Muy mala idea para hacer esto. Si el hilo de llamada es diferente, no funcionará. Además, está poniendo hilos en HashMap pero no los está eliminando. – xmen

5

Una solución podría ser colocar una clase simple public dentro de AsyncTask (asegúrese de que la tarea que define también sea public) que tiene un método public que llama a publishProgress(val). Pasar esa clase debe estar disponible desde cualquier otro paquete o clase.

public abstract class MyClass { 

    public MyClass() { 
     // code... 
    } 

    // more code from your class... 

    public class Task extends AsyncTask<String, Integer, Integer> { 
     private Progress progress; 

     protected Task() { 
      this.progress = new Progress(this); 
     } 

     // ... 

     @Override 
     protected Integer doInBackground(String... params) { 
      // ... 
      SomeClass.doStuff(progress); 
      // ... 
     } 

     // ... 

     @Override 
     protected void onProgressUpdate(Integer... progress) { 
      // your code to update progress 
     } 

     public class Progress { 
      private Task task; 

      public Progress(Task task) { 
       this.task = task; 
      } 

      public void publish(int val) { 
       task.publishProgress(val); 
      } 
     } 
    } 
} 

y luego en la otra clase:

public class SomeClass { 
    public static void doStuff(Progress progress){ 
     // do stuff 
     progress.publish(20); 
     // more stuff etc 
    } 
} 

Esto funcionó para mí.

+0

+1 por la respuesta perfecta ... funcionó para mí ... Gracias :) –

Cuestiones relacionadas