2013-05-27 10 views
23

Ich versuche herauszufinden, warum Android AsyncTask gibt Parameter über execute und warum die Übergabe an den Konstruktor scheint nicht getan werden (in der Dokumentation, zumindest).Sollte ich dem Konstruktor oder AsyncTask.execute (params) Parameter geben?

Dies ist die Art und Weise, die mir scheint am meisten Sinn zu machen (natürlich, die eigentliche Aufgabe mit arbeite ich mehr als nur eine Summe Rechner):

public class FooTask extends AsyncTask<Void, Integer, Long> { 
    private ProgressBar progressBar; 
    private int[] data; 

    public FooTask(ProgressBar progressBar, int... data) { 
     this.progressBar = progressBar; 
     this.data = data; 
    } 

    protected void onPreExecute() { 
     progressBar.setMax(data.length); 
     progressBar.setProgress(0); 
    } 

    protected Long doInBackground(Void... _) { 
     long sum = 0; 
     for (int i = 0; i < data.length; i++) { 
      sum += data[i]; 
      publishProgress(i); 
     } 
     return sum; 
    } 

    protected void onProgressUpdate(Integer... progress) { 
     progressBar.setProgress(progress[0]); 
    } 

    protected void onPostExecute(Long result) { 
     Log.i(TAG, "Sum: " + result); 
    } 
} 

Diese so würden verwendet:

new FooTask(progressBar, 1, 2, 3).execute(); 

Dies ist jedoch nicht die Art, wie die Dokumentation darüber spricht; es nutzt Argumente execute(), wie dies (genommen, um die extremen keinen Konstruktor überhaupt mit, aber immer noch ein Feld verwenden, weil sonst wäre es zu schrecklich sein):

public class FooTask extends AsyncTask<Object, Integer, Long> { 
    private ProgressBar progressBar; 
    private boolean isMaxSettingUpdate = true; 

    protected Long doInBackground(Object... params) { 
     progressBar = params[0]; 
     long sum = 0; 
     for (int i = 1; i < data.length; i++) { 
      sum += (int) data[i]; 
      publishProgress(i - 1, data.length); 
     } 
     return sum; 
    } 

    protected void onProgressUpdate(Integer... progress) { 
     progressBar.setMax(progress[1]); 
     progressBar.setProgress(progress[0]); 
    } 

    protected void onPostExecute(Long result) { 
     Log.i(TAG, "Sum: " + result); 
    } 
} 

Die Ausführung dieser Aufgabe mehr aussehen würde, wie folgt aus:

new FooTask().execute(progressBar, 1, 2, 3); 

eine andere Möglichkeit, die ich an den Konstruktor die Fortschrittsbalken betrachtet gab und die Daten zu dem Anruf ausführen, aber dann kann ich immer noch nicht onPreExecute als ich den maximalen Wert nicht kennen. (Ich würde lieber ein echtes Max verwenden, anstatt den Maximalwert willkürlich festzulegen und den Prozentsatz zu berechnen ... sieht einfach schöner aus.)

Wo ist das Gleichgewicht? Was soll ich machen? Gibt es irgendetwas falsch mit dem Konstruktor?

Antwort

11

Warum die Docs alles in der Methode tun, kann als Ergebnis ihrer Beispielwahl sein. Normalerweise ist es wahrscheinlicher, dass Sie Ihre AsyncTask erweitern und nur doInBackground() verwenden, nicht den Konstruktor.

Anyways, das documentation states:

Speicher observability


AsyncTask garantiert, dass alle Rückrufe in solchen eine Art und Weise synchronisiert sind, dass die folgenden Operationen ohne explizite Synchronisationen sicher sind.

• Setzen Sie die Elementfelder im Konstruktor oder onPreExecute() und beziehen Sie sich auf sie in> doInBackground (Params ...).

• Setzen Sie Mitgliedsfelder in doInBackground (Params ...) und beziehen Sie sich auf diese in OnProgressUpdate (Progress ...) und OnPostExecute (Result).

Das bedeutet, dass Sie mit beiden Ansätzen gut sein sollten.

Und als Randnotiz habe ich eine AsyncTask mit einem param-ed Konstruktor ohne Probleme verwendet, so dass ich die Dokumentation sichern kann.

Auch für Ihren speziellen Fall, wenn der ProgressBar zuvor einen anderen maximalen Wert hatte, sollte er standardmäßig auf 100 gesetzt werden.Dies bedeutet, dass Sie Ihren Konstruktor nur in ProgressBar akzeptieren lassen können und doInBackground() in den Daten akzeptieren (die auch eine Mitgliedsvariable sein sollten). Dann, wenn der Fortschritt der Aktualisierung tun

(progress[0]/data.length) * 100 

Es ist nicht perfekt sein wird, und Sie können konvertieren zu verdoppeln, wenn Sie die Genauigkeit erhöhen möchten, aber dies sollte der Code am einfachsten verstehen machen.

+0

sein Wie würden Sie über den Fortschrittsbalken Sache gehen? Ist es angebracht, es so weiterzugeben? –

+0

Da Sie 'onPreExecute()' benötigen, würde ich mit Ihrem ersten, nur Konstruktor Ansatz gehen, es erfüllt die Anforderung und es ist einfacher zu lesen. Sie müssen jedoch auf Orientierungsänderungen achten. Da Aktivitäten/Fragmente neu erstellt werden, behält Ihre Aufgabe in diesem Fall eine alte Referenz der Fortschrittsleiste bei, sodass die Leiste für den Benutzer nicht aktualisiert werden sollte. –

+0

Oh! Zeigt, wie wenig ich verstehe, wie Android intern funktioniert. Es war mir nie in den Sinn gekommen zu denken, dass sie vor und nach dem gleichen Objekt wären. Ich sehe also, dass das Überschreiten des Fortschrittsbalkens * nicht * sicher ist. Danke, dass du das gelöscht hast! –

2

Meiner Meinung nach, wenn ich einige Werte übergeben möchte, um die UI zu initialisieren, genau wie ProgressBar, das Sie erwähnten, muss ich es in OnPreExecute() tun. Und da die OnPreExecute() keine Parameter akzeptieren, setze ich Werte im Konstruktor lieber als Parameter ein. Wenn einige Werte nicht relativ zur UI sind, brauchen Sie nur in doInBackground, zum Beispiel die Download-URLs von Dateien, die ich als Parameter übergeben werde.

Also, in einer Zusammenfassung, wenn die Werte über UI sind, übergeben Sie sie im Konstruktor, , wenn Werte nur in doInBackground verwendet werden, übergeben Sie sie als Parameter von execute.

0

Wenn Sie im Hauptthread arbeiten (das erfordert Parameter), müssen Sie diese Parameter an den Konstruktor übergeben.

Alles, was Sie über .execute() übergeben, wird im async-Thread ausgeführt.

Wie buptcoder erwähnt, in erster Linie wird die UI Sachen betreffen, sondern auch disk/Datenbankoperationen können Thread kritische

Verwandte Themen