2014-10-23 2 views
4

Я получаю эту ошибку: java.lang.IllegalStateException: Содержимое адаптера изменилось, но ListView не получил уведомление. Убедитесь, что содержимое адаптера не изменено из фонового потока, но только из потока пользовательского интерфейса. [в ListView (2131034188, класс android.widget.ListView) с адаптером (класс .MainActivity $ ListAdapter)]android - Содержимое адаптера изменилось, но ListView не получил уведомление

Это то, что я делаю, я запускаю 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(); 
       }}); 
     } 
    } 

и после этого мой списокView построен. Я протестировал его на нескольких разных устройствах, но у них не было проблем, но некоторые пользователи сказали мне, и я зашел в систему и увидел эту ошибку.

Что делать, чтобы решить эту проблему? что не так ?

благодарит вас

+0

Почему 'runOnUiThread (...)'? вы обычно можете вызвать 'ladap.notifyDataSetChanged()' – Rustam

+0

'onPostExecute (...)' запускается непосредственно в потоке 'UI'. – Rustam

+2

Можете ли вы показать 'doInBackground()' тоже? Более конкретно, вы можете модифицировать 'contactList' там, который предположительно по-прежнему привязан к адаптеру. Это может, кстати, привести к незаконному состоянию, когда адаптер получает доступ, когда он был/изменен в фоновом потоке и до того, как «onPostExexute()» будет удалено. –

ответ

6

Извиняюсь за запоздалый ответ, но это выглядит как подозреваемый в моем предыдущем комментарии: вы изменяете contactList из двух разных потоков.

ListView кэширует количество элементов и всякий раз, когда ему приходится компоновать свои дочерние элементы, он будет проверять это количество снова на текущее количество элементов в связанном адаптере. Если счетчик изменился и 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() + ")]"); 
} 

Source.

Другими словами: это именно то, что вы получаете. В вашем сценарии разница счетчика вызвана изменением набора данных поддержки из более чем одного потока, что приводит к проблеме синхронизации: фоновый поток может изменять набор данных, приостанавливаться, а нить ui затем может вызывать layoutChildren(), не будучи уведомление об изменениях набора данных.

Теперь на решение. Самый простой из них - убедиться, что вы не изменяете список, связанный с адаптером ListView из разных потоков. То есть, либо сделайте копию, которую вы затем можете свободно изменить, либо выделите новый список.

Итак, сделайте что-нибудь подобное, то есть в методе onPreExecute()AsyncTask.

Новый список:

List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>(); 

A (неглубоко) копия:

List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>(contactList); 

Затем в doInBackground(), добавить данные в mNewContactList. Наконец, в onPostExecute(), вы можете:

  1. Создать новый адаптер с помощью mNewContactList и установить его в ListView.
  2. Или (если вы не сделали копию первоначального списка) добавьте содержимое mNewContactList к уже существующему contactList и позвоните по телефону notifyDatasetChanged() по телефону ListView.
Смежные вопросы

 Смежные вопросы