为什么我的值没有从内部类添加到全局 ArrayList 中?

huangapple go评论73阅读模式
英文:

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

问题

我从服务器获取数据,并在每次循环执行时创建QuizFormat类的对象。我在一个全局声明的ArrayList中尝试将这些对象添加进去。我在一个名为getTest()的单独方法中进行此任务,该方法具有内部类,并且该内部类扩展了AsyncTask类,因为在其中执行获取数据的后台任务。问题是,对象未被添加到列表中。我尝试创建一个单独的方法通过将对象作为参数传递来添加对象,但没有成功。我该如何解决这个问题?以下是我正在使用的代码。

QuizFormat是一个基本类,具有构造函数、获取器和设置器:

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;
    }
    // 获取器和设置器在下面
    ...
    ...
}

以下是TestActivity类,在该类中我执行了上述所有描述的任务:

public class TestActivity extends AppCompatActivity {
    ArrayList<QuizFormat> quizFormatList; // 全局 ArrayList
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        
        quizFormatList = new ArrayList<QuizFormat>(); // 初始化 ArrayList
        getTest(); // 调用方法来获取并添加元素
        
        // 检查最终的大小
        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++){
                    // 在这里解析 JSON 数据,跳过了这部分
                    
                    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");
                    
                    // 添加到 ArrayList
                    quizFormatList.add(new QuizFormat(que_id, question, options, answer));
                }
                // 检查 ArrayList 的大小
                Log.i("quizFormatList", "size: "+quizFormatList.size());
            }
        }
        // 执行类
        FetchData fd = new FetchData();
        fd.execute();
    }
}

以下是日志输出。

这是从内部类'FetchData'中计算的大小:

I/quizFormatList: size: 5

以下是在调用getTest()后在onCreate()方法中计算的大小,如上述代码所示:

I/size: ArrayList: 0

我该如何将数据添加到列表中,并如何访问它?需要帮助。

英文:

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

                    int que_id = testObj.getInt(&quot;que_id&quot;);
                    String question = testObj.getString(&quot;question&quot;);
                    String answer = testObj.getString(&quot;answer&quot;);
					
					//Adding to ArrayList
					quizFormatList.add(new QuizFormat(que_id, question, options, answer));
				}
				//checking ArrayList size
				Log.i(&quot;quizFormatList&quot;, &quot;size: &quot;+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.

答案1

得分: 2

这是一个经典的异步与同步行为问题。在这里发生的情况是,您调用了异步方法getTest,然后立即访问了quizFormatList。问题在于在getTest有机会填充它之前,您就访问了quizFormatList。不幸的是,不能这样做,有时可能会起作用,有时可能不会起作用。

针对这个问题有几种解决方案,让我尝试提供一个不需要对您现有代码进行太多更改的解决方案。首先,我们希望异步任务在完成工作时实际上通知主活动。我们将使用回调来实现:

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) {
    // 做与之前完全相同的操作,填充 quizFormat 字段

    callback.asyncWorkDone(quizFormatList);
  }
}

因此,我们现在有一个异步任务,它需要创建一个回调,并在完成时使用正确的quizFormatList结果调用此回调。

以下是从活动中使用它的方法:

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

    new FetchData((results) -> {
        // 使用 results 进行所需操作
    }).execute();
  }   
}

上述代码希望能够正常工作并满足您的需求,但我必须指出,在Android上使用异步任务被认为是危险的,且不被推荐。关于这个话题本身就需要另一篇答案来进行解释,但我现在不打算详细讨论,因为您的问题并不一定专门与异步任务相关,而是与异步环境中的竞态条件有关。不过,您可以看到,如果主活动被销毁(例如手机旋转),当前运行的异步任务传递的回调将属于一个已销毁的活动,根据您对结果的处理方式,可能会尝试访问无法再次访问的内容。这只是这种方法的问题之一。您可以在网上阅读更多相关信息,有足够的资料可供参考,但是这里有一篇非常好的文章,由Dan Lew撰写。

英文:

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&lt;QuizFormat&gt; quizFormatList);
}
class FetchData extends AsyncTask&lt;Void,Void,String&gt; {
private final List&lt;QuizFormat&gt; quizFormatList = new ArrayList&lt;&gt;();
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) -&gt; {
// do whatever you need to do with results
}).execute();
}   
}    

<hr/>

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, i.e.) 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.

huangapple
  • 本文由 发表于 2020年8月28日 17:46:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/63631348.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定