android - 适配器的内容已更改但ListView未收到通知

Nav*_*rab 4 android android-listview android-asynctask

我收到此错误:java.lang.IllegalStateException:适配器的内容已更改但ListView未收到通知.确保不从后台线程修改适配器的内容,而只是从UI线程修改.[在ListView(2131034188,类android.widget.ListView)中使用Adapter(类.MainActivity $ ListAdapter)]

这就是我做的,我运行一个AsyncTask并获取json数据,然后,onPostExecute,我调用ListAdapter将数据转换为listview.

@Override
        protected Void doInBackground(Void... arg0) {
            Spots_tab1_json sh = new Spots_tab1_json();
            String jsonStr = sh.makeServiceCall(url + page, Spots_tab1_json.GET);

            if (jsonStr != null) {
JSONObject jsonObj = new JSONObject(jsonStr);
                    contacts = jsonObj.getJSONArray(TAG_CONTACTS);
for (int i = 0; i < contacts.length(); i++) {
                        JSONObject c = contacts.getJSONObject(i);
                        String onvan = new String(c.getString("onvan").getBytes("ISO-8859-1"), "UTF-8");
                        String id = new String(c.getString("id").getBytes("ISO-8859-1"), "UTF-8");
                        String dates = new String(c.getString("dates").getBytes("ISO-8859-1"), "UTF-8");
                        String price = new String(c.getString("gheymat").getBytes("ISO-8859-1"), "UTF-8");
                        HashMap<String, String> contact = new HashMap<String, String>();
                        contact.put("onvan", onvan);
                        contact.put("img", new String(c.getString("img").getBytes("ISO-8859-1"), "UTF-8"));
                        contactList.add(contact);
                    }
}
}
} 

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
            if (!isCancelled() && goterr == false) {
            final ListAdapter ladap=new ListAdapter(MainActivity.this, contactList);
            lv.setAdapter(ladap);
            runOnUiThread(new Runnable() {
                public void run() {
                    ladap.notifyDataSetChanged();
                }});
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后,我的listView被构建.我已经在几个不同的设备上测试了它,没有它们有任何问题,但有些用户告诉我,我登录并看到这个错误.

我该怎么做才能解决它?怎么了 ?

谢谢

MH.*_*MH. 7

迟来的回复道歉,但在我之前的评论中似乎有疑问:你是contactList从两个不同的线程修改的.

ListView缓存元素计数,每当有布局的孩子,它会检查该计数againt在绑定的适配器项目的当前数量.如果计数已更改ListView且未通知此情况,则会引发错误:

else if (mItemCount != mAdapter.getCount()) {
    throw new IllegalStateException("The content of the adapter has changed but "
    + "ListView did not receive a notification. Make sure the content of "
    + "your adapter is not modified from a background thread, but only from "
    + "the UI thread. Make sure your adapter calls notifyDataSetChanged() "
    + "when its content changes. [in ListView(" + getId() + ", " + getClass()
    + ") with Adapter(" + mAdapter.getClass() + ")]");
}
Run Code Online (Sandbox Code Playgroud)

来源.

换句话说:这正是你得到的错误.在您的方案中,计数差异是由多个线程修改后备数据集引起的,导致同步问题:后台线程可能会修改数据集,被挂起,然后ui线程可能会在layoutChildren()没有得到通知的情况下调用关于数据集的变化.

现在,进入解决方案.最简单的方法是确保不ListView从不同的线程修改绑定到适配器的列表.也就是说,要么制作可以随意修改的副本,要么分配新的列表.

所以,做这样的事情,即在onPreExecute()方法中AsyncTask.

新名单:

List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>();
Run Code Online (Sandbox Code Playgroud)

一个(浅)副本:

List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>(contactList);
Run Code Online (Sandbox Code Playgroud)

然后,在doInBackground(),添加数据mNewContactList.最后,onPostExecute()你可以:

  1. 使用创建新适配器mNewContactList并将其设置为ListView.
  2. 或者(如果你没有做原始列表的副本)的内容添加mNewContactList到已经存在contactList,并呼吁notifyDatasetChanged()ListView.