2010-12-06 8 views
49

Ich möchte meine UI von einem Thread aktualisieren, die eine Progressbar aktualisiert. Leider, wenn die Fortschrittsbalken aus dem "runnable" Zeichen aktualisiert werden, verschwindet die Fortschrittsleiste! Ändern der Fortschrittsleisten in onCreate() auf der anderen Seite funktioniert!Update UI von Thema

irgendwelche Vorschläge?

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

Antwort

9

Sie benötigen ein Handler im UI-Thread erstellen und dann die Benutzeroberfläche zu veröffentlichen oder eine Nachricht von Ihrem anderen Thread senden verwenden, um

11

Verwenden Sie die AsyncTask Klasse zu aktualisieren (statt Runnable). Es hat eine Methode namens onProgressUpdate, die die Benutzeroberfläche beeinflussen kann (sie wird im UI-Thread aufgerufen).

11

Wenn Sie Handler verwenden (ich sehe, Sie tun und hoffentlich erstellten Sie seine Instanz auf dem UI-Thread), dann verwenden Sie nicht runOnUiThread() innerhalb Ihrer runnable. runOnUiThread() verwendet, wenn Sie smth aus einem Nicht-UI-Thread tun, wird jedoch Handler bereits Ihre runnable auf UI-Thread ausgeführt werden.

Versuchen smth wie dies zu tun:

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

Sie dies mit Hilfe von AsyncTask (ein intelligenter Hintergrund-Thread) und ProgressDialog

AsyncTask Verwendung des UI-Thread ermöglicht korrekte und einfach tun sollten. Mit dieser Klasse können Hintergrundoperationen ausgeführt und Ergebnisse im UI-Thread veröffentlicht werden, ohne dass Threads und/oder Handler manipuliert werden müssen.

Eine asynchrone Aufgabe wird durch eine Berechnung festgelegt, die auf einem Hintergrund-Thread ausgeführt wird, und dessen Ergebnis wird auf dem UI-Thema veröffentlicht. Eine asynchrone Task wird durch 3 generische Typen definiert, genannt Params, Progress und Result, und 4 Schritte, genannt begin, doInBackground, processProgress und end.

Die 4 Schritte

Wenn eine asynchrone Aufgabe ausgeführt wird, geht die Aufgabe, über 4 Stufen:

onPreExecute(), auf dem UI-Thread aufgerufen wird, unmittelbar nachdem die Aufgabe ausgeführt wird. Dieser Schritt wird normalerweise zum Einrichten der Aufgabe verwendet, z. B. durch Anzeigen einer Fortschrittsleiste in der Benutzeroberfläche.

doInBackground(Params...), auf dem Hintergrund-Thread aufgerufen unmittelbar nach OnPreExecute() beendet die Ausführung. Dieser Schritt wird verwendet, um eine Hintergrundberechnung durchzuführen, die sehr lange dauern kann. Die Parameter der asynchronen Task werden an diesen Schritt übergeben. Das Ergebnis der Berechnung muss von diesem Schritt zurückgegeben werden und wird an den letzten Schritt zurückgegeben. Dieser Schritt kann auch publishProgress (Progress ...) verwenden, um eine oder mehrere Fortschrittseinheiten zu veröffentlichen. Diese Werte werden im Schritt onProgressUpdate (Progress ...) im UI-Thread veröffentlicht.

onProgressUpdate(Progress...), aufgerufen, auf dem UI-Thread nach einem Aufruf publishProgress (Progress ...). Der Zeitpunkt der Ausführung ist nicht definiert. Diese Methode wird verwendet, um jede Art von Fortschritt in der Benutzerschnittstelle anzuzeigen, während die Hintergrundberechnung noch ausgeführt wird. Zum Beispiel kann damit eine Fortschrittsleiste animiert oder Protokolle in einem Textfeld angezeigt werden.

onPostExecute(Result), wird auf dem UI-Thread aufgerufen, nachdem die Hintergrundberechnung abgeschlossen ist. Das Ergebnis der Hintergrundberechnung wird diesem Schritt als Parameter übergeben. Threading Regeln

Es gibt ein paar Threading Regeln, die befolgt werden müssen, für diese Klasse richtig funktioniert:

Die Aufgabe Instanz auf dem UI-Thread erstellt werden müssen. ausführen (Params ...) muss im UI-Thread aufgerufen werden. Rufen Sie onPreExecute(), onPostExecute (Result), doInBackground (Params ...), onProgressUpdate (Progress ...) nicht manuell auf. Die Aufgabe kann nur einmal ausgeführt werden (eine Ausnahme, wenn eine zweite Ausführung versucht wird, geworfen werden.)

Beispielcode
Was der Adapter in diesem Beispiel tut, ist nicht wichtig, wichtiger zu verstehen, dass Sie brauchen Verwenden von AsyncTask, um einen Dialog für den Fortschritt anzuzeigen.

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 sollte idealerweise für kurze Operationen (ein paar Sekunden am meisten.) Verwendet werden [...]" - http://developer.android.com/ reference/android/os/AsyncTask.html –

1

Wenn Sie die AsyncTask nicht mögen, können Sie die observer pattern verwenden. In diesem Beispiel verwenden Sie den ResponseHandler als innere Klasse in Ihrer Aktivität und dann eine String - Nachricht, die den Fortschrittsbalken - Prozentsatz festlegt ... Sie müssten sicherstellen, dass Änderungen an der Benutzeroberfläche innerhalb des ResponseHandler durchgeführt werden, um das Einfrieren des UI, dann kann Ihr Arbeitsthread (EventSource im Beispiel) die erforderlichen Aufgaben ausführen.

Ich würde die AsyncTask tho verwenden, aber das Beobachtermuster kann gut für Anpassungsgründe sein, und es ist einfacher zu verstehen. Ich bin mir auch nicht sicher, ob dieser Weg allgemein akzeptiert wird oder zu 100% funktionieren wird. Im Download und das Android-Plugin jetzt, um es zu testen

23

Die einfachste Lösung, die ich gesehen habe, um eine kurze Ausführung zum UI-Thread zu liefern, ist über die post() -Methode einer Ansicht. Dies wird benötigt, da die UI-Methoden nicht neu sind. Die Methode hierfür ist:

package android.view; 

public class View; 

public boolean post(Runnable action); 

Die post() Methode zum SwingUtilities.invokeLater entspricht(). Leider habe ich nichts Einfaches gefunden, das der SwingUtilities.invokeAndWait() entspricht, aber man kann das spätere basierend auf dem ehemaligen mit einem Monitor und einem Flag erstellen.

Was Sie damit speichern, ist die Erstellung eines Handlers. Sie brauchen einfach , um Ihre Ansicht zu finden und dann darauf zu posten. Sie können Ihre Ansicht über findViewById() finden, wenn Sie dazu neigen, mit id-ed-Ressourcen zu arbeiten. Die sich ergebende Code ist sehr einfach:

/* inside your non-UI thread */ 

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

Hinweis: Im Vergleich zu SwingUtilities.invokeLater() Die Methode View.post() führt eine Boolesche zurück, der angibt, ob die Ansicht, die eine zugeordnete Ereigniswarteschlange aufweist. Da ich die invokeLater() bzw. post() trotzdem nur für Feuer und vergessen, Ich habe den Ergebniswert nicht überprüft. Grundsätzlich sollten Sie post() aufrufen, nachdem onAttachedToWindow() auf der Ansicht aufgerufen wurde.

Mit freundlichen Grüßen

+0

Wenn "Ansicht" eine Referenz auf ein tatsächliches Objekt wie "Fortschrittsbalken" ist, würde das Zurücklassen einer Aktivität oder eines Fragments, das die gesamte Ansicht erstellt, die Ressource 'view' recyceln und' null' verursachen .post (~); – Stallman

+0

Wie Sie den Looper über einen Post töten können, finden Sie hier: http://stackoverflow.com/a/30562809/502187 –

0

Wie offizielle documentation empfohlen, können Sie AsyncTaskWorkitems kürzer als 5 ms Dauer zu behandeln. Wenn Ihre Aufgabe länger dauert, suchen Sie nach anderen Alternativen.

HandlerThread ist eine Alternative zu Thread oder AsyncTask. Wenn Sie die Benutzeroberfläche von HandlerThread aktualisieren müssen, senden Sie eine Nachricht auf dem UI-Thread Looper, und der UI-Thread Handler kann UI-Aktualisierungen verarbeiten.

Beispielcode:

Android: Toast in a thread