¿Cómo puede pasar múltiples parámetros primitivos a AsyncTask?

82

Hay preguntas relacionadas, como ¿Cómo puedo pasar 2 parámetros a una clase AsyncTask? , pero me encontré con la dificultad de intentar en vano pasar múltiples primitivas como parámetros a una AsyncTask, así que quiero compartir lo que descubrí. Esta sutileza no se refleja en las preguntas y respuestas existentes, así que quiero ayudar a cualquiera que se encuentre con el mismo problema que yo y evitarle el dolor.

La pregunta es la siguiente: tengo varios parámetros primitivos (por ejemplo, dos longs) que quiero pasar a una AsyncTask para que se ejecute en segundo plano, ¿cómo se puede hacer? (Mi respuesta ... después de luchar con esto por un tiempo ... se puede encontrar a continuación).

robguinness
fuente

Respuestas:

153

Simplemente envuelva sus primitivas en un contenedor simple y páselo como un parámetro AsyncTask, así:

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

Llámalo así:

MyTaskParams params = new MyTaskParams(foo, bar, arple);
MyTask myTask = new MyTask();
myTask.execute(params);
David Wasser
fuente
Esto también funciona. Pero creo que las opciones anteriores son más simples.
robguinness
10
Esto me parece mucho mejor en realidad. He estado usando la forma Object ... params de hacerlo y, por alguna razón, no se siente bien o no es seguro hacerlo.
Mafro34
1
Trabajando como un amigo encantador .. ¡Gracias por compartir ..!
Deepak S. Gavkar
2
Muy elegante, me encanta.
Sipty
1
@DavidWasser: gracias por su actualización, ¡además de que la solución dada funciona muy bien!
Moussa
93

Otra forma: solo necesita agregar el constructor MyTask en su clase 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
}

Luego llame

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

Una segunda forma como la respuesta de David Wasser.

malajisi
fuente
9
de hecho, esta es mi forma favorita de las tres para pasar argumentos de diferentes tipos. Sin Objeto para lanzar y sin necesidad de crear una clase adicional.
QuickFix
3
¡Necesitas llamar a super () en tu constructor anulado!
zyamys
2
@zyamys ¿por qué es necesario super () aquí? Según tengo entendido, se llamará automáticamente. Vea aquí stackoverflow.com/a/2054040/984263
carthurs
De las opciones de este hilo, este es el que más me gusta. Sin clases auxiliares, esta clase se puede usar por sí sola y describe sus propios requisitos. No se pasan objetos misteriosos. Gracias.
Vinnyq12
Esto funciona. Se siente muy sucio, pero soy demasiado vago para probar otra cosa. No puedo creer que no haya una forma nativa agradable de hacer esto de forma nativa. Posiblemente podría ser compatible más tarde en Java<(String,Object,int),Void,Void>
Sirens
82

(Estrictamente hablando) NO es posible pasar múltiples primitivas a AsyncTask. Por ejemplo, si desea realizar myTask.execute(long1, long2)e intentar configurar private class myTask extends AsyncTask<long, Void, Void>con el método correspondiente:

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

su IDE probablemente se quejará de la necesidad de anular un método de supertipo. Tenga en cuenta que está utilizando la denominada firma del método Varargs para doInBackground, donde (long... params)es como decir "Acepto un número variable de longs, almacenado como una matriz llamada params. No entiendo completamente qué causa que se presente una queja de compilador / IDE , pero creo que tiene que ver con cómo Paramsse define la clase genérica .

En cualquier caso, es posible lograr lo que desea sin ningún problema, siempre que envíe correctamente sus primitivas a sus respectivas envolturas no primitivas (por ejemplo, int => Integer, long => Long, etc.). En realidad, no es necesario convertir explícitamente sus primitivas en no primitivas. Java parece manejar eso por ti. Solo necesita configurar su ASyncTask de la siguiente manera (para el ejemplo de 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]
    }
    ...
}

Luego puede usar esta clase como lo pretendía originalmente, por ejemplo:

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

O para cualquier número de primitivas que desee, SIEMPRE QUE SEAN DEL MISMO TIPO. Si necesita pasar varios tipos de primitivas, esto también se puede hacer, pero deberá modificar lo anterior para:

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

    }
    ...
}

Esto es más flexible, pero requiere convertir explícitamente los parámetros a sus respectivos tipos. Si esta flexibilidad no es necesaria (es decir, un solo tipo de datos), recomiendo ceñirse a la primera opción, ya que es un poco más legible.

robguinness
fuente
También puede usar lo siguiente para el método protected LocationItemizedOverlay doInBackground(Object[] objects) y agregar lo siguiente para la definición de tarea asíncrona. private class MyTask extends AsyncTask<Object, Void, Void>
Hany Sakr
8

El método de ejecución integrado acepta una matriz de Params , pero todos deben ser del tipo definido ... así que si simplemente configuras el tipo de PARAM en OBJECT , puedes pasar lo que quieras siempre que sean hijos de objetos. ...

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

Luego, en su doInBackGround, simplemente lanza cada parámetro para volver a lo que necesita que sea:

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

Y tu ejecución es simplemente:

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

No es tan buena como envolverlo en su propia clase contenedora, o un paquete, o hash o algo, porque depende del orden en ambos lados, pero funcionará. Por supuesto, podría convertir su matriz en un parámetro de HashMap (,) y básicamente está implementando un paquete personalizado en ese punto, pero funcionará.

Speckpgh
fuente
1
¡La gente aquí es genial con tantas técnicas geniales y esta fue mi favorita!
George Udosen
7

Me gusta el método de malajisi, pero si no lo hiciste, ¿no podrías usar la clase Bundle?

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

Luego, simplemente pase el paquete y descomprímalo dentro de MyTask. ¿Es esta una idea terrible? Evita crear una clase personalizada y es flexible si decide que necesita pasar parámetros adicionales más adelante.

Actualización: Han pasado bastantes años desde que escribí esta respuesta, y ahora realmente no me gusta. Recomendaría no usar un paquete. Si necesita pasar varios parámetros a un asynctask (o cualquier cosa, en realidad), use una clase personalizada que contenga todos sus parámetros a la vez. Usar un paquete es una buena solución a un problema que no debería tener. No existe ninguna ley que prohíba la creación de una clase personalizada para contener exactamente lo que necesita, y nada más.

Además, ¿por qué no estás usando corrutinas? Asynctasks son tan 2014.

MrPlow
fuente
Creo que está bien
BQuadra
Gran idea. Sin embargo, supongo que esto no admite que los objetos personalizados se pasen a doInBackground ()
John Ward
1

Esto se resuelve mediante subclases. Google tiene un ejemplo para resolver este problema (subclases) en la documentación oficial de Android AsyncTask:

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

Ejemplo:

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
fuente