简体   繁体   中英

Why my values are not being added to global ArrayList from the inner class

I get data from server and I am creating the object of class QuizFormat for each time the loop executes. I have a global ArrayList declared in which I am trying to add these objects. I am doing this task in a separate method getTest() which has inner class and it is extending the AsyncTask class, as the background task of fetching the data is carried out in it. The problem is, the objects are not being added to the list. I tried creating a separate method to add objects by passing them as parameters, but not a luck. How can i fix this? Here is the code I am using. QuizFormat , a basic class with constructor, getters and setters:

public class QuizFormat {
    int id;
    String question;
    String[] options;
    String answer;

    public QuizFormat(int id, String question, String[] options, String answer) {
        this.id = id;
        this.question = question;
        this.options = options;
        this.answer = answer;
    }
    //getters and setters below
    ...
    ...
}

Following is the TestActivity class, in which i am doing all the above described task:

public class TestActivity extends AppCompatActivity {
     ArrayList<QuizFormat> quizFormatList; //Global ArrayList
      @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        
        quizFormatList = new ArrayList<QuizFormat>(); // Initializing ArrayList
        getTest(); //call to the method to fetch and add elements
        
        //checking final size
        Log.i("size","ArrayList: "+ quizFormatList.size());
        try {
            for (QuizFormat qf: quizFormatList) {
                Log.i("que",qf.getQuestion());
            }
        } catch (NullPointerException e){
            e.printStackTrace();
        }
    }
    
    public void getTest(){
        class FetchData extends AsyncTask<Void,Void,String> {
            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                
                for (int i=0; i<testArray.length(); i++){
                    //Parsing JSON data here, skipping that part
                    
                    String[] options = new String[4];
                    options[0] = optionsObj.getString("opt1");
                    options[1] = optionsObj.getString("opt2");
                    options[2] = optionsObj.getString("opt3");
                    options[3] = optionsObj.getString("opt4");

                    int que_id = testObj.getInt("que_id");
                    String question = testObj.getString("question");
                    String answer = testObj.getString("answer");
                    
                    //Adding to ArrayList
                    quizFormatList.add(new QuizFormat(que_id, question, options, answer));
                }
                //checking ArrayList size
                Log.i("quizFormatList", "size: "+quizFormatList.size());
            }
        }
        //Executing class
        FetchData fd = new FetchData();
        fd.execute();
    }
}

Following is the logcat output. This is the size in counted from inner class 'FetchData'

I/quizFormatList: size: 5

and the following one is the size counted in onCreate() method after calling getTest() , as shown in the code above.

I/size: ArrayList: 0

How can i get that data in the list, and how to access it? Help needed.

This is a classic problem of async vs sync behavior. What's happening here is that you are calling the method getTest which is asynchronous and accessing right after that quizFormatList . The problem is that you access quizFormatList before getTest has a chance to populate it. Unfortunately, this cannot be done like that and also, sometimes it might work, and sometimes it might not.

There are several solutions for this, let me try and provide one that doesn't require too many changes to what you have now. First, we want the async task to actually notify the main activity when it's done with its work. We will do this with a callback:

interface OnAsyncWorkDoneCallback {
  void asyncWorkDone(List<QuizFormat> quizFormatList);
}

class FetchData extends AsyncTask<Void,Void,String> {
  private final List<QuizFormat> quizFormatList = new ArrayList<>();
  private final OnAsyncWorkDoneCallback callback;

  public FetchData(OnAsyncWorkDoneCallback callback) {
    this.callback = callback;
  }

  @Override
  protected void onPostExecute(String s) {
    // Do exactly what you were doing before and populate the field quizFormat

    callback.asyncWorkDone(quizFormatList);
  }
}

So what we have is an async task that requires now a callback to be created and once it's done it'll call this callback with the correct results for quizFormatList .

Here's how you use it from the activity:

public class TestActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);

    new FetchData((results) -> {
        // do whatever you need to do with results
    }).execute();
  }   
}    

The code above will hopefully work and serve your needs, but I have to say that using async tasks on android is considered dangerous and highly discouraged. This topic is another whole answer by itself and I didn't want to address it right now, since your problem is not necessarily specific to async tasks, but to race conditions in an asynchronous environment. However, you can see that if the main activity gets destroyed (the phone rotates, ie) the callback passed to the currently running async task will belong to a destroyed activity and depending on what you do with the results it might end up trying to access things it can no longer access. This is just one of the issues with this approach. You can read online about all of it, there's more than enough information on it, but here's a really good post by Dan Lew.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM