0

我正在调用ASyncTask更新我的适配器,并且在填充我的列表时偶尔会遇到此运行时错误

E/AndroidRuntime(16197): java.lang.IllegalStateException: 适配器的内容已经改变,但ListView没有收到通知。确保适配器的内容不是从后台线程修改的,而只是从 UI 线程修改的。

从后台线程填充我的适配器是一个坏主意,就像错误消息所暗示的那样?

我正在打电话notifyDataSetChanged()onPostExecute()但是当我打电话时我也遇到了同样的错误onProgressUpdate()

这是我的异步任务。假设 fetchOneItem() 从项目列表中获取单个项目,填写名称/描述,然后在下一次迭代中移动到下一个项目。

private class GetItems extends AsyncTask<Integer, Integer, Integer> {
  @Override
  protected void onPreExecute() {
    super.onPreExecute();
    pd = new ProgressDialog(context);
    pd.setTitle("Displaying...");
    pd.setMessage("Please wait.");
    pd.setCancelable(false);
    pd.setIndeterminate(true);
    pd.show();
    TextView myMsg = (TextView)findViewById(R.id.topMsg);
    myMsg.setText("Displaying items...");
  }

  protected Integer doInBackground(Integer... params) {
    // TODO Auto-generated method stub
    do {
      fetchOneItem(); // fetches 1 item
      myList.add(new Item(name, description));
      Log.i(TAG, "Adding " + name + ", " + desc + ", adapter size = " + myAdapter.getCount());
      publishProgress(i);
    } while (doneFetching != true);
    Log.i(TAG, "Completed loading, i =" + i);
    return i;
  }

  protected void onProgressUpdate(Integer... values) {
    TextView myMsg = (TextView)findViewById(R.id.topMsg);
    myMsg.setText("Displaying " + Integer.toString(values[0].intValue()) + " items");
  }

  @Override
  protected void onPostExecute(Integer numItems) {
    TextView myMsg = (TextView)findViewById(R.id.topMsg);
    myMsg.setText("Displayed " + numItems + " items");
    myAdapter.notifyDataSetChanged();
    allItemsLoaded = true;
    Log.i(TAG, "Adapter size = " + myAdapter.getCount());
    pd.dismiss();
  }
}
4

3 回答 3

0

是的,如您所见,android 不允许您这样做。如果您想更新任何视图,您应该在 UI 线程中进行。但是你也不应该在 UI 线程中使用慢速操作,例如网络通信,这在 android 中是不允许的。事实上,你应该尽可能缩短 UI 代码,因为如果时间太长,android 会认为你的应用程序已经崩溃。

编辑:我认为您使用的是 ArrayAdapter,它会在您更新它时通知更改。

所以你有两个选择:

使用您的 ArrayAdapter 但在 UI 线程中更新它。

或者实现您自己的 BaseAdapter 子类,并且您做得对。

ArrayAdapter 适用于简单的情况,通常使用静态数据集。

于 2013-10-16T03:06:43.853 回答
0

如我所见,您正在尝试“实时”更新列表视图,对吗?

好吧,我不知道怎么做,所以我建议你实现一个刷新按钮来调用这个AsyncTask。如果您想向用户显示您实际上正在更新列表视图,则发送 DialogProgress 很重要。希望能帮助到你!

于 2013-10-16T02:38:36.030 回答
0

发布较晚,但可能会帮助面临类似情况的人。

问题:您有一个长时间运行的操作,并希望将其带到后台线程。同样在操作完成后,您想更新 UI 线程上的视图。

解决方案:

//spawn a new thread for background processing.
    new Thread(new Runnable() {
        @Override
        public void run() {
            //starting your background operation.
            //this is a network call.
            final Location location = BackendApi.builder().getSlaveLocation(mActivity.getApplicationContext(), "xyz", null);

            //now i want to update views on UI thread.
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    //this code will run on UI thread.
                    myView.setText("Latitude: " + location.getLatitude());
                }
            });
        }
    }).start();

在更新视图/适配器之前,确保活动没有被破坏。

于 2017-11-04T16:40:08.590 回答