近日在做项目时遇到非必现crush,具体异常信息为:
// Short Msg: java.lang.IllegalStateException
// Long Msg: java.lang.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. [in ListView(2131624004, class android.widget.ListView) with Adapter(class com.baidu.smartcalendar.widget.cx)]
// Build Label: Coolpad/pxa1088dkb_def/8750:4.2.1/JOP40D/4.2.063.P1.131218.8750:user/release-keys
// Build Changelist: 4.2.016.P1.8750
// Build Time: 1387361194000
// java.lang.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. [in ListView(2131624004, class android.widget.ListView) with Adapter(class com.baidu.smartcalendar.widget.cx)]
// at android.widget.ListView.layoutChildren(ListView.java:1559)
// at android.widget.AbsListView.onTouchModeChanged(AbsListView.java:3313)
// at android.view.ViewTreeObserver.dispatchOnTouchModeChanged(ViewTreeObserver.java:712)
// at android.view.ViewRootImpl.ensureTouchModeLocally(ViewRootImpl.java:3078)
// at android.view.ViewRootImpl.ensureTouchMode(ViewRootImpl.java:3062)
// at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3206)
// at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3169)
// at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4300)
// at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4279)
// at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4371)
// at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:179)
// at android.os.MessageQueue.nativePollOnce(Native Method)
// at android.os.MessageQueue.next(MessageQueue.java:125)
// at android.os.Looper.loop(Looper.java:124)
// at android.app.ActivityThread.main(ActivityThread.java:5078)
// at java.lang.reflect.Method.invokeNative(Native Method)
// at java.lang.reflect.Method.invoke(Method.java:511)
// at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:856)
// at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:623)
// at dalvik.system.NativeStart.main(Native Method)
异常的描述是不要在其他线程中修改listview的adapter数据而在ui线程中进行刷新notifyDataSetChanged()。我们一般为adapter添加数据时常常使用activity类内部的全局变量,这时在外部或其他线程中更新数据时,如果不及时刷新listview,就会抛出上述异常。于是我去掉线程,直接在ui线程中更新数据,发现问题依然存在。
仔细查看代码,我的代码是这样的:
private void prepareData(Calendar c) { mTodayList = new ArrayList<SCEvent>(); mFutureList = new ArrayList<SCEvent>(); mList = new ArrayList<SCEvent>(); Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(System.currentTimeMillis()); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); ArrayList<SCEvent> list = SmartCalendarDB.getInstance(getActivity()) .getBirthdayLatest2(mCalendar.getTimeInMillis(), cal.getTimeInMillis() != mCalendar.getTimeInMillis()); if(list != null && list.size() > 0) { int month = mCalendar.get(Calendar.MONTH) + 1; int day = mCalendar.get(Calendar.DAY_OF_MONTH); for(SCEvent e : list) { if(e.getmBirthdayMonth() == month && e.getmBirthdayDay() == day) { mTodayList.add(e); } else { mFutureList.add(e); } } if(mTodayList != null && mTodayList.size() > 0) { Collections.sort(mTodayList, new TimeComparator()); for(SCEvent e : mTodayList) { mList.add(e); } mList.add(null); } if(mFutureList != null && mFutureList.size() > 0) { if(mTodayList == null || mTodayList.size() <= 0) { mList.add(null); } Collections.sort(mFutureList, new AdvanceComparator()); boolean add = mList != null && mList.size() > 0; for(SCEvent e : mFutureList) { mList.add(e); } if(!add) { mList.add(null); } } } if(mList != null && mList.size() > 0) { mListView.setVisibility(View.VISIBLE); mEmptyLayout.setVisibility(View.GONE); mAdapter.notifyDataSetChanged(); } else { mListView.setVisibility(View.GONE); mEmptyLayout.setVisibility(View.VISIBLE); } }
我的adapter数据是保存在mlist中,而在代码中不断的改变了mlist内部的数据,而只在最后调用了一次mAdapter.notifyDataSetChanged(),导致listview没有及时刷新而抛出异常。
所以结论在使用listview时,及时不在非ui线程中更新adapter数据,也最好将数据直接定义在adapter类内部或在改变数据时要及时刷新listview,否则会抛出上述异常。