How can I return an ArrayList from my AsyncTask?

13.5k views Asked by At

I have an AsynkTask method on my MainActivity that it's the following:

class chargeCars extends AsyncTask<Void, Integer, ArrayList<Car>> {
        protected void onPreExecute(){
        }
        protected ArrayList<Car> doInBackground(Void... params) {

            ArrayList<Car> cars = new ArrayList<Car>();

            cars.add(new Car(1,"Car1");
            cars.add(new Car(2,"Car2");
            cars.add(new Car(3,"Car3");

            return cars;
        }

        protected void onProgressUpdate(){
        }

        protected void onPostExecute(ArrayList<Car> c){

        }
    }

where Car it's an object that I have declared in another Class and which constructor it's:

public Car(int idCar, String name)
{
    this.idCar = idCar;
    this.name = name;
}

and I know that return cars; returns the ArrayList named cars to the method onPostExecute but I tried to retrieve this ArrayList from there and I couldn't because it always said to me that onPostExecute method have to be Void.

I have also another ArrayList declared in my MainActivity class:

ArrayList<Car> totalCars = new ArrayList<Car>();

so what I tried it's to clone cars ArrayList into my totalCars ArrayList in my onPostExecute method but it gives me an error in which it says that:

'clone()' has protected access in 'java.lang.Object'

Here it's the code trying to clone my ArrayList:

protected void onPostExecute(ArrayList<Car> c)
{
    for(Car cr: c)
    {
        totalCars.add(cr.clone());
    }
}

So I also tried to add my values directly to my totalCars ArrayList on my doInBackground method:

totalCars.add(new Car(1,"Car1");
totalCars.add(new Car(2,"Car2");
totalCars.add(new Car(3,"Car3");

but when I execute my AsyncTask method:

new chargeCars().execute();

the size of my totalCars ArrayList it's equals to 0.

What should I do? I'm totally block and I searched in SO for an answer but I couldn't find anything that helps to me.

EDIT: My Car Class it's:

public class Car {

    private int idCar;
    private String name;

    public Car(){};

    public Car(int idCar, String name)
    {
        this.idCar = idCar;
        this.name = name;
    }

    public void setIdCar(int idCar)
    {
         this.idCar = idCar;
    }

    public int getIdCar()
    {
        return idCar;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }
}

EDIT 2: I put some logs to see what exactly does my program and I got strange results:

As Clairvoyant said, I use both of his methods but any of them works. What could I saw? Well, I saw that in the onPostExecute method the ArrayList contains values in both of the methods. Here there is no problem.

The problem it's that when I execute the AsyncTask in the method onCreate of my MainActivity class, my ArrayList totalCars doesn't have any value inside, but in the method onPostExecute of my AsyncTask it has all the values that I put inside the ArrayList.

What could be the problem?

EDIT 3: I also tried with:

public class MainActivity extends ActionBarActivity {

   ArrayList<Car> cars = new ArrayList<Car>();

   protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new chargeCars().execute();
    Log.d("Size onCreate: ", "" + cars.size()); //It returns me a 0 -->Incorrect
   }

   class chargeCars extends AsyncTask<Void, Integer, Boolean> {
        protected void onPreExecute(){
        }

        protected ArrayList<Car> doInBackground(Void... params) {

            cars.add(new Car(1,"Car1");
            cars.add(new Car(2,"Car2");
            cars.add(new Car(3,"Car3");

            Log.d("Size: ", "" + cars.size()); //It gives me the size equals to 3 -->Correct 
            return true;

        }

        protected void onProgressUpdate(){
        }

        protected void onPostExecute(boolean c){

        }
    }
}

But still doesn't work. I also tried to make a ProgressDialog to be secure that my AsyncTask it's finished when I try to use my Log in my onCreate method but it also gives me an error (I have another question in SO about it but I don't know if it's a good practice in SO to make directly reference from another question so I don't post it here).

Am I doing something wrong? Why in my doInBackground it gives me the correct size of the ArrayList and in my onCreate method not?

Thanks in advance!

2

There are 2 answers

1
Blackbelt On BEST ANSWER

Am I doing something wrong? Why in my doInBackground it gives me the correct size of the ArrayList and in my onCreate method not?

Because the execution is asynchronous and the UI Thread doesn't way the end of the execution of your AsyncTask to continue its execution flow. To get the results back, when your AsyncTask finishes its execution, without blocking the UI Thread, you could use a delegate/lister.

Let's declare an interface on the top of your Activity, and let't it implement this interface (you'll need to implement the methods). When onCarsCompleted is invoked you'll be able to print the correct size of your ArrayList

 public class MainActivity extends ActionBarActivity implements OnCarsListener {
      public interface OnCarsListener {
          void onCarsCompleted(ArrayList<Car> cars);
          void onCarsError(String error);
      }

      // the other code

      public void onCarsCompleted(ArrayList<Car> cars) {
             Log.d("Size: ", "" + cars.size()); 
      }

      public void onCarsError(String error) {

      }  

}         

now we add a constructor to your AsyncTask, which takes as parameter an object of type OnCarsListener, and we keep a member reference to the object in the scope of chargeCars, and onPostExecute we communicate back to the Activity the results of your computation:

class chargeCars extends AsyncTask<Void, Integer, ArrayList<Car>> {

    private final OnCarsListener mListener;
    ArrayList<Car> cars = new ArrayList<Car>();

    public chargeCars(OnCarsListener listener) {
         mListener = listener;
    } 

    protected void onPreExecute(){
    }

    protected ArrayList<Car> doInBackground(Void... params) {

        cars.add(new Car(1,"Car1");
        cars.add(new Car(2,"Car2");
        cars.add(new Car(3,"Car3");

        Log.d("Size: ", "" + cars.size()); //It gives me the size equals to 3 -->Correct 
        return cars;

    }

    protected void onProgressUpdate(){
    }

    protected void onPostExecute(ArrayList<Car> cars){
         if (mListener != null) {
              mListener.onCarsCompleted(cars);
         } 
    }


}

And of course, you need to instantiate the AsyncTask like

new chargeCars(this).execute();

12
Pankaj On

Method 1: Don't use clone just use object to add:

protected void onPostExecute(ArrayList<Car> c)
{
       super.onPostExecute();
    for(Car cr: c)
    {
        totalCars.add(cr);
    }
       //Code for further execution
}

Method 2: Make a method which will be called by onPostExceute() after it has got ArrayList

 protected void onPostExecute(ArrayList<Car> c)
    {
     super.onPostExecute();
       setValues(c);
    }

private void setValues(ArrayList<Car> c){
      for(Car cr: c)
        {
            totalCars.add(cr);
        }
 //Code for further execution
}