Jak można przekazać wiele parametrów pierwotnych do AsyncTask?

82

Istnieją powiązane pytania, takie jak Jak mogę przekazać 2 parametry do klasy AsyncTask? , ale napotkałem trudności, próbując na próżno przekazać wiele prymitywów jako parametrów do AsyncTask, więc chcę podzielić się tym, co odkryłem. Ta subtelność nie znajduje odzwierciedlenia w istniejących pytaniach i odpowiedziach, dlatego chcę pomóc każdemu, kto napotka ten sam problem, co ja, i uratować mu ból.

Pytanie jest takie: mam wiele parametrów pierwotnych (np. Dwa długie), które chcę przekazać do AsyncTask w celu wykonania w tle - jak to zrobić? (Moją odpowiedź ... po dłuższej walce z tym ... można znaleźć poniżej.)

robguinness
źródło

Odpowiedzi:

154

Po prostu zawiń swoje prymitywy w prosty kontener i przekaż go jako parametr do AsyncTask, na przykład:

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

Nazwij to tak:

MyTaskParams params = new MyTaskParams(foo, bar, arple);
MyTask myTask = new MyTask();
myTask.execute(params);
David Wasser
źródło
To też działa. Ale uważam, że powyższe opcje są prostsze.
robguinness
10
To wydaje mi się o wiele przyjemniejsze. Używam metody Object ... params i z jakiegoś powodu nie jest to dobre ani bezpieczne.
Mafro34
1
Pracuję jak czarujący kumpel… Dzięki za udostępnienie…!
Deepak S. Gavkar
2
Bardzo eleganckie, uwielbiam to.
Sipty
1
@DavidWasser: dziękuję za aktualizację, poza tym podane rozwiązanie działa świetnie!
Moussa
93

Inny sposób: wystarczy dodać konstruktora MyTask do swojej klasy MyTask:

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
}

Wtedy zadzwoń

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

Drugi sposób, jak odpowiedź Davida Wassera.

malajisi
źródło
9
w rzeczywistości jest to mój ulubiony sposób z całej trójki przekazywania argumentów różnego typu. Brak obiektu do rzucenia i nie ma potrzeby tworzenia dodatkowej klasy.
QuickFix,
3
Musisz wywołać super () w swoim nadpisanym konstruktorze!
zyamys
2
@zyamys dlaczego super () jest tutaj konieczne? Jak rozumiem, zostanie wywołany automatycznie. Zobacz tutaj stackoverflow.com/a/2054040/984263
carthurs
Z opcji w tym wątku to mi się najbardziej podoba. Brak klas pomocniczych, ta klasa może być używana samodzielnie i opisuje własne wymagania. Żadnych tajemniczych obiektów. Dzięki.
Vinnyq12
To działa. Czuję się bardzo brudny, ale jestem zbyt leniwy, żeby spróbować czegoś innego. Nie mogę uwierzyć, że nie ma przyjemnego, rodzimego sposobu, aby to zrobić natywnie. Może być obsługiwane później w Javie<(String,Object,int),Void,Void>
Sirens
82

Nie jest (ściśle mówiąc) możliwe przekazanie wielu prymitywów do AsyncTask. Na przykład, jeśli chcesz wykonać myTask.execute(long1, long2)i spróbować skonfigurować private class myTask extends AsyncTask<long, Void, Void>odpowiednią metodę:

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

Twoje IDE prawdopodobnie będzie narzekać na konieczność zastąpienia metody nadrzędnego typu. Zauważ, że używasz tak zwanej sygnatury metody Varargs dla doInBackground, gdzie (long... params)jest jak powiedzenie "Akceptuję zmienną liczbę długich znaków, przechowywaną jako tablica zwana params. Nie do końca rozumiem, co powoduje zgłoszenie skargi kompilatora / IDE , ale myślę, że ma to związek ze sposobem Paramszdefiniowania klasy ogólnej .

W każdym razie możliwe jest osiągnięcie tego, co chcesz, bez problemu, pod warunkiem, że poprawnie rzutujesz swoje prymitywy na odpowiadające im niepierwotne opakowania (np. Int => Integer, long => Long itp.). Właściwie nie musisz jawnie rzutować swoich prymitywów na inne niż prymitywy. Wydaje się, że Java sobie z tym poradzi. Wystarczy skonfigurować ASyncTask w następujący sposób (na przykład długie pozycje):

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

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

Następnie możesz użyć tej klasy zgodnie z pierwotnymi zamierzeniami, np .:

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

Lub dla dowolnej liczby prymitywów, POD warunkiem, że SĄ TEGO SAMEGO TYPU. Jeśli chcesz przekazać wiele typów prymitywów, możesz to również zrobić, ale będziesz musiał zmodyfikować powyższe, aby:

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];

    }
    ...
}

Jest to bardziej elastyczne, ale wymaga jawnego rzutowania parametrów na ich odpowiednie typy. Jeśli ta elastyczność nie jest potrzebna (np. Pojedynczy typ danych), polecam trzymać się pierwszej opcji, ponieważ jest ona nieco bardziej czytelna.

robguinness
źródło
Możesz również użyć następujących metod dla metody protected LocationItemizedOverlay doInBackground(Object[] objects) i dodać następujące elementy dla definicji zadania asynchronicznego. private class MyTask extends AsyncTask<Object, Void, Void>
Hany Sakr
8

Wbudowana metoda execute akceptuje tablicę Params , ale wszystkie muszą być określonego typu ... więc jeśli po prostu ustawisz typ PARAM na OBJECT , możesz przekazać cokolwiek chcesz, o ile są one elementami potomnymi obiektów. ...

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

Następnie w swoim doInBackGround, po prostu rzucasz każdy parametr, aby wrócić do tego, czego potrzebujesz:

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

A twoje wykonanie to po prostu:

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

Nie jest tak dobra jak umieszczenie go w swojej własnej klasie opakowującej, pakiecie, haszyszu lub czymś podobnym, ponieważ jesteś zależny od kolejności od obu stron, ale to zadziała. Oczywiście możesz po prostu uczynić swoją tablicę parametrem HashMap (,) iw zasadzie w tym momencie możesz niestandardowo implementować pakiet, ale to zadziała.

Speckpgh
źródło
1
Ludzie tutaj są tacy fajni z tak wieloma fajnymi technikami i to był mój ulubiony!
George Udosen
7

Podoba mi się metoda malajisi, ale jeśli nie, to czy nie możesz użyć klasy Bundle?

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

Następnie po prostu podaj pakiet i rozpakuj go w MyTask. Czy to okropny pomysł? Unikasz tworzenia klasy niestandardowej i jest elastyczna, jeśli zdecydujesz, że musisz później przekazać dodatkowe parametry.

Aktualizacja: Minęło sporo lat, odkąd napisałem tę odpowiedź i teraz naprawdę mi się to nie podoba. Odradzałbym używanie pakietu. Jeśli chcesz przekazać wiele parametrów do zadania asynchronicznego (lub cokolwiek, tak naprawdę), użyj niestandardowej klasy, która przechowuje wszystkie parametry naraz. Korzystanie z pakietu to dobre rozwiązanie problemu, którego nie powinieneś mieć. Nie ma prawa zakazującego tworzenia niestandardowej klasy, która zawierałaby dokładnie to, czego potrzebujesz i nic więcej.

Poza tym, dlaczego nie używasz programów? Asynctasks to taki rok 2014.

MrPlow
źródło
Świetny pomysł. Zakładam jednak, że nie obsługuje to przekazywania obiektów niestandardowych do doInBackground ()
John Ward
1

Jest to rozwiązywane za pomocą podklas. Google ma przykład rozwiązania tego problemu (podklasy) w oficjalnej dokumentacji Android AsyncTask:

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

Przykład:

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
źródło