2010-12-06 8 views
49

Quiero actualizar mi UI desde un Thread que actualiza una barra de progreso. Lamentablemente, al actualizar el dibujable de la barra de progreso del "ejecutable", la barra de progreso desaparece. ¡Cambiando el drawable de progressbars en onCreate() en el otro lado funciona!Update UI from Thread

¿Alguna sugerencia?

public void onCreate(Bundle savedInstanceState) { 
    res = getResources(); 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.gameone); 
    pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); //**Works**/ 
    handler.postDelayed(runnable, 1);  
} 

private Runnable runnable = new Runnable() { 
    public void run() { 
     runOnUiThread(new Runnable() { 
      public void run() 
      { 
       //* The Complete ProgressBar does not appear**/       
       pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); 
      } 
     }); 
    } 
} 

Respuesta

9

Es necesario crear un Handler en el hilo de interfaz de usuario y luego usarlo para publicar o enviar un mensaje desde el otro hilo para actualizar la interfaz de usuario

11

Utilice la clase (en lugar de Ejecutable) AsyncTask. Tiene un método llamado onProgressUpdate que puede afectar a la interfaz de usuario (se invoca en el hilo de la interfaz de usuario).

11

Si utiliza Handler (veo que haces y es de esperar que creó su instancia en el hilo de interfaz de usuario), entonces no utilice runOnUiThread() interior de su runnable. runOnUiThread() se usa cuando se hace smth desde un subproceso no relacionado con la interfaz de usuario, sin embargo, Handler ya ejecutará su runnable en el subproceso de interfaz de usuario.

Tratar de hacer algo bajo la siguiente manera:

private Handler mHandler = new Handler(); 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.gameone); 
    res = getResources(); 
    // pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); **//Works** 
    mHandler.postDelayed(runnable, 1); 
} 

private Runnable runnable = new Runnable() { 
    public void run() { 
     pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); 
     pB.invalidate(); // maybe this will even not needed - try to comment out 
    } 
}; 
60

Usted debe hacer esto con la ayuda de AsyncTask (un hilo apaisada inteligente) y ProgressDialog

AsyncTask permite el uso adecuado y fácil del hilo de interfaz de usuario. Esta clase permite realizar operaciones en segundo plano y publicar resultados en el subproceso de la interfaz de usuario sin tener que manipular subprocesos y/o controladores.

Una tarea asincrónica se define mediante un cálculo que se ejecuta en un subproceso en segundo plano y cuyo resultado se publica en el subproceso de la interfaz de usuario. Una tarea asincrónica está definida por 3 tipos genéricos, llamados Params, Progress y Result, y 4 pasos, llamados begin, doInBackground, processProgress y end.

los 4 pasos

Cuando se ejecuta una tarea asíncrona, la tarea pasa a través de 4 pasos:

onPreExecute(), invocados en el subproceso de interfaz de usuario inmediatamente después de que se ejecute la tarea. Este paso se usa normalmente para configurar la tarea, por ejemplo, al mostrar una barra de progreso en la interfaz de usuario.

doInBackground(Params...), invocado en el subproceso de fondo inmediatamente después de que onPreExecute() termine de ejecutarse. Este paso se usa para realizar un cálculo de fondo que puede llevar mucho tiempo. Los parámetros de la tarea asíncrona se pasan a este paso. El resultado del cálculo debe ser devuelto por este paso y será devuelto al último paso. Este paso también puede usar publishProgress (Progress ...) para publicar una o más unidades de progreso. Estos valores se publican en el subproceso UI, en el paso onProgressUpdate (Progreso ...).

onProgressUpdate(Progress...), invocado en el hilo de la IU después de una llamada a publishProgress (Progreso ...). El momento de la ejecución no está definido. Este método se usa para mostrar cualquier forma de progreso en la interfaz de usuario mientras el cálculo de fondo aún se está ejecutando. Por ejemplo, se puede usar para animar una barra de progreso o mostrar registros en un campo de texto.

onPostExecute(Result), invocado en el hilo de la interfaz de usuario una vez que finaliza el cálculo de fondo. El resultado del cálculo de fondo se pasa a este paso como un parámetro. reglas de roscado

Hay algunas reglas de roscado que se deben seguir para esta clase funcione correctamente:

La instancia de tarea se debe crear en el hilo de interfaz de usuario. execute (Params ...) debe invocarse en el subproceso de UI. No invoque onPreExecute(), onPostExecute (Result), doInBackground (Params ...), onProgressUpdate (Progress ...) manualmente. La tarea se puede ejecutar sólo una vez (una excepción se produce si se intenta una segunda ejecución.)

código Ejemplo
Lo que hace el adaptador en este ejemplo no es lo importante, más importante entender que usted necesita para usar AsyncTask para mostrar un diálogo para el progreso.

private class PrepareAdapter1 extends AsyncTask<Void,Void,ContactsListCursorAdapter > { 
    ProgressDialog dialog; 
    @Override 
    protected void onPreExecute() { 
     dialog = new ProgressDialog(viewContacts.this); 
     dialog.setMessage(getString(R.string.please_wait_while_loading)); 
     dialog.setIndeterminate(true); 
     dialog.setCancelable(false); 
     dialog.show(); 
    } 
    /* (non-Javadoc) 
    * @see android.os.AsyncTask#doInBackground(Params[]) 
    */ 
    @Override 
    protected ContactsListCursorAdapter doInBackground(Void... params) { 
     cur1 = objItem.getContacts(); 
     startManagingCursor(cur1); 

     adapter1 = new ContactsListCursorAdapter (viewContacts.this, 
       R.layout.contact_for_listitem, cur1, new String[] {}, new int[] {}); 

     return adapter1; 
    } 

    protected void onPostExecute(ContactsListCursorAdapter result) { 
     list.setAdapter(result); 
     dialog.dismiss(); 
    } 
} 
+5

"AsyncTasks idealmente debería ser usado para operaciones de corto (unos pocos segundos en la mayoría.) [...]" - http://developer.android.com/ reference/android/os/AsyncTask.html –

1

Si no te gusta el AsyncTask podría utilizar el observer pattern. En ese ejemplo, use ResponseHandler como una clase interna en su actividad y luego tenga un mensaje de cadena que establecerá el porcentaje de barras de progreso ... Debería asegurarse de que cualquier alteración en la UI se realice dentro del ResponseHandler para evitar congelar el UI, entonces su hilo de trabajo (EventSource en el ejemplo) puede realizar las tareas requeridas.

Yo usaría el AsyncTask aunque, sin embargo, el patrón del observador puede ser bueno por razones de personalización, además de que es más fácil de entender. Además, no estoy seguro si esta forma es ampliamente aceptada o funcionará al 100%. Estoy descargando y el complemento de Android ahora para probarlo

23

La solución más simple que he visto para proporcionar una ejecución corta de al subproceso de la interfaz de usuario es a través del método de una publicación post(). Esto es necesario ya que los métodos de IU no son reentrantes. El método de esto es:

package android.view; 

public class View; 

public boolean post(Runnable action); 

el método POST() corresponde a la SwingUtilities.invokeLater(). Lamentablemente no encontré nada simple que corresponde a SwingUtilities.invokeAndWait(), pero uno puede construir el posterior basado en el primero con un monitor y un indicador.

Así que lo que ahorras con esto es crear un controlador. Simplemente necesita para encontrar su vista y luego publicar en ella. Puede encontrar su vista a través del findViewById() si tiende a trabajar con recursos id-ed. El código resultante es muy simple:

/* inside your non-UI thread */ 

view.post(new Runnable() { 
     public void run() { 
      /* the desired UI update */ 
     } 
    }); 
} 

Nota: En comparación con SwingUtilities.invokeLater() el método View.post() hace devolver un booleano, que indica si la vista tiene una cola de eventos asociado. Desde que utilicé el invokeLater() resp. publicar() de todos modos solo para disparar y olvidar, No revisé el valor del resultado. Básicamente debe llamar a post() solo después de que se haya llamado a ontachedToWindow() en la vista.

Saludos

+0

Si 'view' es una referencia a un objeto real como' progress bar', al dejar una actividad o fragmento que crea toda la vista se reciclaría el recurso 'view', lo que provocaría' null' .post (~); – Stallman

+0

Para saber cómo matar el looper a través de una publicación, consulte aquí: http://stackoverflow.com/a/30562809/502187 –

0

según lo recomendado por el oficial documentation, puede utilizar AsyncTaskpara manejar los elementos de trabajo de menos de 5 ms de duración. Si su tarea toma más tiempo, busque otras alternativas.

HandlerThread es una alternativa a Thread o AsyncTask. Si necesita actualizar la interfaz de usuario desde HandlerThread, publique un mensaje en UI Thread Looper y UI Thread Handler puede manejar actualizaciones de IU.

código Ejemplo:

Android: Toast in a thread