Wie können Sie mehrere primitive Parameter an AsyncTask übergeben?

82

Es gibt verwandte Fragen, z. B. Wie kann ich zwei Parameter an eine AsyncTask-Klasse übergeben? Aber ich hatte die Schwierigkeit, vergeblich zu versuchen, mehrere Grundelemente als Parameter an eine AsyncTask zu übergeben, und möchte daher mitteilen, was ich entdeckt habe. Diese Subtilität wird in den vorhandenen Fragen und Antworten nicht erfasst, daher möchte ich jedem helfen, der auf dasselbe Problem stößt wie ich, und ihm den Schmerz ersparen.

Die Frage ist folgende: Ich habe mehrere primitive Parameter (z. B. zwei Longs), die ich an eine AsyncTask übergeben möchte, um sie im Hintergrund auszuführen. Wie kann das gemacht werden? (Meine Antwort ... nachdem ich eine Weile damit zu kämpfen hatte ... ist unten zu finden.)

Robguinness
quelle

Antworten:

153

Wickeln Sie einfach Ihre Grundelemente in einen einfachen Container und übergeben Sie diesen als Parameter an AsyncTask:

private static class MyTaskParams {
    int foo;
    long bar;
    double arple;

    MyTaskParams(int foo, long bar, double arple) {
        this.foo = foo;
        this.bar = bar;
        this.arple = arple;
    }
}

private class MyTask extends AsyncTask<MyTaskParams, Void, Void> {
    @Override
    protected void doInBackground(MyTaskParams... params) {
        int foo = params[0].foo;
        long bar = params[0].bar;
        double arple = params[0].arple;
        ...
    }
}

Nennen Sie es so:

MyTaskParams params = new MyTaskParams(foo, bar, arple);
MyTask myTask = new MyTask();
myTask.execute(params);
David Wasser
quelle
Das funktioniert auch. Aber ich glaube, die oben genannten Optionen sind einfacher.
Robguinness
10
Das kommt mir eigentlich viel schöner vor. Ich habe die Object ... params-Methode verwendet und aus irgendeinem Grund fühlt es sich einfach nicht gut oder sicher an.
Mafro34
1
Arbeiten wie Charm Buddy .. Danke fürs Teilen ..!
Deepak S. Gavkar
2
Sehr elegant, liebe es.
Sipty
1
@ DavidWasser: Danke für dein Update, außerdem funktioniert die angegebene Lösung super!
Moussa
93

Ein anderer Weg: Sie müssen lediglich den MyTask-Konstruktor in Ihre MyTask-Klasse einfügen:

private class MyTask extends AsyncTask<String, Void, Void> {
    int foo;
    long bar;
    double arple;

    MyTask(int foo, long bar, double arple) { 
         // list all the parameters like in normal class define
        this.foo = foo;
        this.bar = bar;
        this.arple = arple;
    }
    ......   // Here is doInBackground etc. as you did before
}

Dann ruf an

new MyTask(int foo, long bar, double arple).execute();

Ein zweiter Weg wie David Wassers Antwort.

Malajisi
quelle
9
Eigentlich ist dies meine Lieblingsmethode der drei, um Argumente verschiedener Typen zu übergeben. Kein Objekt zum Wirken und keine Notwendigkeit, eine zusätzliche Klasse zu erstellen.
QuickFix
3
Sie müssen super () in Ihrem überschriebenen Konstruktor aufrufen!
Zyamys
2
@zyamys warum ist super () hier notwendig? Soweit ich weiß, wird es automatisch aufgerufen. Siehe hier stackoverflow.com/a/2054040/984263
carthurs
Von den Optionen in diesem Thread gefällt mir das am besten. Keine Hilfsklassen, diese Klasse kann alleine verwendet werden und beschreibt ihre eigenen Anforderungen. Keine geheimnisvollen Objekte werden herumgereicht. Vielen Dank.
Vinnyq12
Das funktioniert. Fühlt sich sehr schmutzig an, aber ich bin zu faul, um etwas anderes auszuprobieren. Ich kann nicht glauben, dass es keinen guten einheimischen Weg gibt, dies nativ zu tun. Könnte möglicherweise später in Java unterstützen<(String,Object,int),Void,Void>
Sirenen
82

Es ist (genau genommen) NICHT möglich, mehrere Grundelemente an AsyncTask zu übergeben. Wenn Sie beispielsweise die entsprechende Methode ausführen myTask.execute(long1, long2)und versuchen möchten, sie einzurichten private class myTask extends AsyncTask<long, Void, Void>:

@Override
protected LocationItemizedOverlay doInBackground(long... params) {...}

Ihre IDE wird sich wahrscheinlich darüber beschweren, dass eine Supertyp-Methode überschrieben werden muss. Beachten Sie, dass Sie das so genannte verwenden varargs Methodensignatur für doInBackground, wo (long... params)wie wenn man sagt : „Ich eine variable Anzahl von Long - Positionen akzeptieren ist, gespeichert als ein Array mit Namen params. Ich verstehe nicht ganz , was bewirkt , dass ein Compiler / IDE Beschwerde angehoben werden , aber ich denke, es hat damit zu tun, wie die generische Klasse Paramsdefiniert ist.

In jedem Fall ist es möglich, problemlos das zu erreichen, was Sie wollen, vorausgesetzt, Sie wandeln Ihre Grundelemente korrekt in ihre jeweiligen nicht-primitiven Wrapper um (z. B. int => Integer, long => Long usw.). Tatsächlich müssen Sie Ihre Grundelemente nicht explizit in Nicht-Grundelemente umwandeln. Java scheint das für Sie zu erledigen. Sie müssen Ihre ASyncTask nur wie folgt einrichten (zum Beispiel für Longs):

private class MyTask extends AsyncTask<Long, Void, Void> {

    @Override
    protected void doInBackground(Long... params) {
        // Do stuff with params, for example:
        long myFirstParam = params[0]
    }
    ...
}

Sie können diese Klasse dann wie ursprünglich beabsichtigt verwenden, z.

MyTask myTask = new MyTask();
myTask.execute(long1, long2);

Oder für eine beliebige Anzahl von Grundelementen, die Sie möchten, vorausgesetzt, sie sind vom gleichen Typ. Wenn Sie mehrere Arten von Grundelementen übergeben müssen, ist dies ebenfalls möglich. Sie müssen jedoch Folgendes ändern:

private class MyTask extends AsyncTask<Object, Void, Void> {

    @Override
    protected void doInBackground(Object... params) {
        // Do stuff with params, for example:
        long myLongParam = (Long) params[0];
        int myIntParam = (Integer) params[1];

    }
    ...
}

Dies ist flexibler, erfordert jedoch das explizite Umwandeln der Parameter in ihre jeweiligen Typen. Wenn diese Flexibilität nicht benötigt wird (dh ein einzelner Datentyp), empfehle ich, bei der ersten Option zu bleiben, da diese etwas besser lesbar ist.

Robguinness
quelle
Sie können für die Methode protected LocationItemizedOverlay doInBackground(Object[] objects) auch Folgendes verwenden und für die Definition der asynchronen Aufgabe Folgendes hinzufügen. private class MyTask extends AsyncTask<Object, Void, Void>
Hany Sakr
8

Die integrierte Ausführungsmethode akzeptiert ein Array von Parametern , aber alle müssen vom definierten Typ sein. Wenn Sie also einfach den PARAM- Typ auf OBJECT setzen , können Sie alles übergeben , was Sie möchten , solange sie untergeordnete Objekte von Objekten sind. ...

private class MyTask extends AsyncTask<Object, Void, Void> {

Dann wandeln Sie in Ihrem doInBackGround einfach jeden Parameter um, um wieder das zu erreichen, was Sie brauchen:

 @Override
 protected void doInBackground(Object... params) {
     Context t = (Context)params[0];
     String a = (String) params[1];
     List<LatLng> list = (List<LatLng>)params[2];
     .
     .
     .

Und Ihre Ausführung ist einfach:

 new MyTask().execute(context,somestring,list_of_points);

Nicht so gut wie das Einwickeln in Ihre eigene Wrapper-Klasse oder ein Bundle oder Hash oder so, weil Sie von beiden Seiten in der Reihenfolge abhängig sind, aber es wird funktionieren. Natürlich können Sie Ihr Array einfach zu einem Parameter von HashMap (,) machen, und Sie implementieren zu diesem Zeitpunkt im Grunde genommen ein Bundle benutzerdefiniert, aber es wird funktionieren.

Speckpgh
quelle
1
Die Leute hier sind so cool mit so vielen coolen Techniken und das war mein Favorit!
George Udosen
7

Ich mag Malajisis Methode, aber wenn Sie es nicht getan haben, könnten Sie dann nicht die Bundle-Klasse verwenden?

 Bundle myBundle = new Bundle();
 myBundle.putInt("foo", foo);
 myBundle.putLong("bar", bar);
 myBundle.putDouble("arple", arple);

Dann übergeben Sie einfach das Bundle und packen es in MyTask aus. Ist das eine schreckliche Idee? Sie vermeiden das Erstellen einer benutzerdefinierten Klasse und es ist flexibel, wenn Sie später zusätzliche Parameter übergeben müssen.

Update: Es ist einige Jahre her, seit ich diese Antwort geschrieben habe, und ich mag sie jetzt wirklich nicht. Ich würde empfehlen, kein Bundle zu verwenden. Wenn Sie mehrere Parameter an eine Asynctask übergeben müssen (oder wirklich irgendetwas), verwenden Sie eine benutzerdefinierte Klasse, die alle Ihre Parameter gleichzeitig enthält. Die Verwendung eines Bundles ist eine gute Lösung für ein Problem, das Sie nicht haben sollten. Es gibt kein Gesetz gegen das Erstellen einer benutzerdefinierten Klasse, die genau das enthält, was Sie benötigen, und sonst nichts.

Warum verwenden Sie keine Coroutinen? Asynctasks sind so 2014.

MrPlow
quelle
Ich finde das schön
BQuadra
Großartige Idee. Ich gehe jedoch davon aus, dass dies nicht die Übergabe von benutzerdefinierten Objekten an doInBackground ()
John Ward am
1

Dies wird über Unterklassen gelöst. Google hat ein Beispiel zur Lösung dieses Problems (Unterklasse) in der offiziellen Android AsyncTask-Dokumentation:

http://developer.android.com/reference/android/os/AsyncTask.html

Beispiel:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
                 // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

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

    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}
Bijan
quelle