问题描述
当数据变化是,没调notifyDataSetChanged方法刷新控件,滑动列表,就会报错如下
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. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131559716, class android.widget.ListView) with Adapter(class kr)]
at android.widget.ListView.layoutChildren(ListView.java:1562)
at android.widget.AbsListView.onTouchMove(AbsListView.java:3919)
at android.widget.AbsListView.onTouchEvent(AbsListView.java:3788)
at android.view.View.dispatchTouchEvent(View.java:8444)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2430)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2158)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2436)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2178)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2436)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2178)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2436)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2178)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2436)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2178)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2436)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2178)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2436)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2178)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2386)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1727)
at android.app.Activity.dispatchTouchEvent(Activity.java:2764)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2335)
at android.view.View.dispatchPointerEvent(View.java:8655)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4238)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4094)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3635)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3694)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3660)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3773)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3668)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3830)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3640)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3694)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3660)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3668)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3640)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5940)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5908)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5872)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6030)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:211)
at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:197)
....
问题代码
public class VideoBroadcastViewsHolder {
......
private ListView mMsgListView;
private List<VideoBroadcast> mMsgDatas;
private BroadcastMsgAdapter mBroadcastMsgAdapter;
...
...
private void updateMsgList(VideoBroadcast videoBroadcast) {
if (TextUtils.isEmpty(videoBroadcast.getMsg())) {
return;
}
if (mMsgDatas == null) {
mMsgDatas = new ArrayList<>();
mBroadcastMsgAdapter = new BroadcastMsgAdapter(mContext,mMsgDatas);
if (mMsgListView != null) {
mMsgListView.setAdapter(mBroadcastMsgAdapter);
}
}
mMsgDatas.add(videoBroadcast);
toRefreshListView(RefreshDelayedTime);
}
private void toRefreshListView(long delayedTime) {
if (mMsgListView != null) {
if (mListRefreshRunnable != null) {
mMsgListView.removeCallbacks(mListRefreshRunnable);
}
DebugLog.d("VideoBroadcastViewsHolder", "toRefreshListView isListViewTouching : " + isListViewTouching + " ,delayedTime :" + delayedTime);
if (isListViewTouching) {
return;
}
if (System.currentTimeMillis() - mLastListRefreshTime >= RefreshIntervalTime) {
mMsgListView.post(new ListRefreshRunnable());
} else {
mListRefreshRunnable = new ListRefreshRunnable();
mMsgListView.postDelayed(mListRefreshRunnable, delayedTime);
}
}
}
......
}
public class BroadcastMsgAdapter extends BaseAdapter {
// 上下文
private Context mContext;
private List<VideoBroadcast> mDatas;
public BroadcastMsgAdapter(Context context ,List<VideoBroadcast> datas) {
mContext = context;
mDatas = datas;
}
...
}
问题分析
当列表适配器直接用的是外面传递进来的列表时,外围列表的管理者对列表进行数据增减,使其数据发生了变化,但应某种原因或者采用的具体策略,没及时通知适配器,在这中间时段,如果用户滑动列表,系统就会报错。
个人建议
让适配器拥有自己的数据列表,这样就可避免类似问题。数据的使用者使者拥有和管理自己的数据,降低耦合性,遵循代码设计原则。
简单修改后,适配器代码如下:
public class BroadcastMsgAdapter extends BaseAdapter {
// 上下文
private Context mContext;
private List<VideoBroadcast> mDatas;
public BroadcastMsgAdapter(Context context) {
mContext = context;
mDatas = new ArrayList<>();
}
public void setDatas(List<VideoBroadcast> datas) {
if (datas != null && datas.size() > 0) {
mDatas.clear();
mDatas.addAll(datas);
}
notifyDataSetChanged();
}
...
}
- 适当调整使用的地方,问题就OK了。*