How can you pass multiple primitive parameters to AsyncTask?
Asked Answered
C

6

84

There are related questions, such as How can I pass in 2 parameters to a AsyncTask class? , but I ran into the difficulty of trying in vain to pass multiple primitives as parameters to an AsyncTask, so I want to share what I discovered. This subtlety is not captured in the existing questions and answers, so I want to help out anyone who runs into the same problem as I did and save them the pain.

The question is this: I have multiple primitive parameters (e.g. two longs) that I want to pass to an AsyncTask to be executed in the background--how can it be done? (My answer...after struggling with this for awhile...can be found below.)

Churchwoman answered 22/8, 2012 at 9:4 Comment(0)
C
83

It is (strictly-speaking) NOT possible to pass multiple primitives to AsyncTask. For example, if you want to perform myTask.execute(long1, long2) and try to set up private class myTask extends AsyncTask<long, Void, Void> with the corresponding method:

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

your IDE will likely complain about needing to override a supertype method. Note that you are using the so-called Varargs method signature for doInBackground, where (long... params) is like saying "I accept a variable number of longs, stored as an array called params. I don't completely understand what causes a compiler/IDE complaint to be raised, but I think it has to do with how the generic class Params is defined.

In any case, it is possible to achieve what you want with no problem, provided you correctly cast your primitives to their respective non-primitive wrappers (e.g. int => Integer, long => Long, etc.). Actually, you don't need to explicitly cast your primitives to non-primitives. Java seems to handle that for you. You just need to set up your ASyncTask as follows (for the example of 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]
    }
    ...
}

You can then use this class as you originally intended, e.g.:

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

Or for any number of primitives that you would like, PROVIDED THEY ARE OF THE SAME TYPE. If you need to pass multiple types of primitives, this can also be done, but you will need to modify the above to:

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

    }
    ...
}

This is more flexible, but it requires explicitly casting the parameters to their respective types. If this flexibility is not needed (i.e. a single data type), I recommend sticking to the first option, as it's slightly more readable.

Churchwoman answered 22/8, 2012 at 9:4 Comment(1)
You can also use the following for the method protected LocationItemizedOverlay doInBackground(Object[] objects) and add the following for the async task definition. private class MyTask extends AsyncTask<Object, Void, Void> Eneidaenema
M
159

Just wrap your primitives in a simple container and pass that as a parameter to AsyncTask, like this:

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

Call it like this:

MyTaskParams params = new MyTaskParams(foo, bar, arple);
MyTask myTask = new MyTask();
myTask.execute(params);
Minnesota answered 22/8, 2012 at 9:23 Comment(5)
This works, too. But I believe the above options are simpler.Churchwoman
This strikes me as a lot nicer actually. I've been using the Object... params way of doing it and for some reason it just doesn't feel good or safe to do.Meitner
Shouldn't the class MyTaskParams be public, or at least less restrictive in order to be able to call it from outside?Drippy
@Mouss that depends. In this case, it is a private inner class used only by the surrounding Activity. Of course, if you want to put it in a separate file then you would need to make it public or package private.Minnesota
@DavidWasser : thanx for your update, besides the given solution works great!Drippy
L
100

Another way: You just need add MyTask constructor in your MyTask class:

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
}

Then call

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

A second way like David Wasser's Answer.

Landes answered 14/1, 2014 at 17:38 Comment(5)
actually this is my favourite way of the three to pass arguments of different types. No Object to cast and no need to create an additional class.Crewelwork
You need to call super() in your overridden constructor!Etymologize
@Etymologize why is super() necessary here? As I understand it, it will be called automatically. See here https://mcmap.net/q/20321/-is-it-unnecessary-to-put-super-in-constructorGranny
Out of the options on this thread I like this the best. No helper classes, this class can be used on its own and describes its own requirements. No mystery Objects being passed around. Thanks.Limbourg
This works. Feels very dirty but I'm too lazy to try anything else. Can't believe there isn't a nice native way to do this natively. Could possibly support later in Java <(String,Object,int),Void,Void>Arvy
C
83

It is (strictly-speaking) NOT possible to pass multiple primitives to AsyncTask. For example, if you want to perform myTask.execute(long1, long2) and try to set up private class myTask extends AsyncTask<long, Void, Void> with the corresponding method:

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

your IDE will likely complain about needing to override a supertype method. Note that you are using the so-called Varargs method signature for doInBackground, where (long... params) is like saying "I accept a variable number of longs, stored as an array called params. I don't completely understand what causes a compiler/IDE complaint to be raised, but I think it has to do with how the generic class Params is defined.

In any case, it is possible to achieve what you want with no problem, provided you correctly cast your primitives to their respective non-primitive wrappers (e.g. int => Integer, long => Long, etc.). Actually, you don't need to explicitly cast your primitives to non-primitives. Java seems to handle that for you. You just need to set up your ASyncTask as follows (for the example of 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]
    }
    ...
}

You can then use this class as you originally intended, e.g.:

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

Or for any number of primitives that you would like, PROVIDED THEY ARE OF THE SAME TYPE. If you need to pass multiple types of primitives, this can also be done, but you will need to modify the above to:

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

    }
    ...
}

This is more flexible, but it requires explicitly casting the parameters to their respective types. If this flexibility is not needed (i.e. a single data type), I recommend sticking to the first option, as it's slightly more readable.

Churchwoman answered 22/8, 2012 at 9:4 Comment(1)
You can also use the following for the method protected LocationItemizedOverlay doInBackground(Object[] objects) and add the following for the async task definition. private class MyTask extends AsyncTask<Object, Void, Void> Eneidaenema
B
9

The built in execute method accepts an array of Params, but they all must be of the defined type.. so if you simply set the PARAM type to OBJECT, then you can pass in whatever you like as long as they are children of objects....

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

Then in your doInBackGround, you simply cast each param in order back to what you need it to be:

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

And your execute is simply:

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

Not as good form as wrapping it in your own wrapper class, or a bundle, or hash or something, because you are order dependent on both sides, but it will work. Of course you could just make your array a param of HashMap(,) and you basically are custom implementing a bundle at that point, but it will work.

Bothwell answered 28/1, 2018 at 3:25 Comment(1)
Folks here are so cool with so many cool technics and this was my favourite!Millesimal
C
7

I like malajisi's method, but if you didn't, couldn't you use the Bundle class?

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

Then just pass the bundle and unpack it inside MyTask. Is this a terrible idea? You avoid creating a custom class, and it's flexible if you decide you need to pass additional parameters later.

Update: It has been quite a few years since I wrote this answer, and I really dislike it now. I would recommend against using a Bundle. If you need to pass multiple parameters into an asynctask (or anything, really), use a custom class that holds all your parameters at once. Using a bundle is a fine solution to a problem you shouldn't have. There is no law against creating a custom class to hold exactly what you need, and nothing else.

Also, why aren't you using coroutines? Asynctasks are so 2014.

Calcific answered 30/1, 2014 at 1:48 Comment(1)
Great idea. I assume however that this doesn't support custom objects from being passed into doInBackground()Beforehand
K
1

This is solved via subclassing. Google has an example for solving this problem (subclassing) in the official Android AsyncTask Documentation:

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

Example:

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");
    }
}
Kliber answered 1/10, 2015 at 13:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.