关于Adapter的The content of the adapter has changed问题分析
来源:互联网 发布:mac办公软件破解版 编辑:程序博客网 时间:2024/06/05 10:12
1、问题描述
07-28 17:22:02.162: E/AndroidRuntime(16779): 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(2131034604, class android.widget.ListView) with Adapter(class com.nodin.sarah.HeartListAdapter)]
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.ListView.layoutChildren(ListView.java:1555)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.AbsListView.onLayout(AbsListView.java:2091)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.View.layout(View.java:14785)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1055)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.View.layout(View.java:14785)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.View.layout(View.java:14785)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1589)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.View.layout(View.java:14785)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1055)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.View.layout(View.java:14785)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.View.layout(View.java:14785)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.View.layout(View.java:14785)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.View.layout(View.java:14785)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewGroup.layout(ViewGroup.java:4631)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1985)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1742)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:998)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5582)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.Choreographer.doCallbacks(Choreographer.java:562)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.Choreographer.doFrame(Choreographer.java:532)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.os.Handler.handleCallback(Handler.java:733)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.os.Handler.dispatchMessage(Handler.java:95)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.os.Looper.loop(Looper.java:137)
07-28 17:22:02.162: E/AndroidRuntime(16779): at android.app.ActivityThread.main(ActivityThread.java:4998)
07-28 17:22:02.162: E/AndroidRuntime(16779): at java.lang.reflect.Method.invokeNative(Native Method)
07-28 17:22:02.162: E/AndroidRuntime(16779): at java.lang.reflect.Method.invoke(Method.java:515)
07-28 17:22:02.162: E/AndroidRuntime(16779): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
07-28 17:22:02.162: E/AndroidRuntime(16779): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
07-28 17:22:02.162: E/AndroidRuntime(16779): at dalvik.system.NativeStart.main(Native Method)
07-28 17:22:02.162: W/ActivityManager(588): Force finishing activity
其中错误描述:
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.
的意思大体是,你的adapter的内容变化了,但是你的ListView并不知情。请保证你adapter的数据在主线程中进行更改!
2、复现场景
使用ListView和Adapter实现动态增删数据列表功能,初始化数据分为两部分:本地和网络。所以在Adapter的数据初始化的时候,先讲本地数据添加到了容器内。同时发起网络请求,等加载完毕后追加到容器内。
问题出现在:当网络请求完毕后追加数据的时候,抛出上述异常。
3、原因分析
Exception解读:
Adapter的数据内容已经改变,但是ListView却未接收到通知。要确保不在后台线程中修改Adapter的数据内容,而要在UI Thread中修改。确保Adapter的数据内容改变时一定要调用notifyDataSetChanged()方法。
且不管Exception内容,先查询Android源码看看该Exception是从哪里抛出来的。
在ListView的layoutChildren()方法里有如下一段方法:
// Handle the empty set by removing all views that are visible
// and calling it a day
if (mItemCount == 0) {
resetList();
invokeOnItemScrollListener();
return;
} 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. in ListView(” + getId() + “, ” + getClass()
+ “) with Adapter(” + mAdapter.getClass() + “)“);
}
亦即,当ListView缓存的数据Count和ListView中Adapter.getCount()不等时,会抛出该异常。
结合开头的异常解读,可以断定肯定是Adapter数据动态更新的问题。仔细检查了自己的代码:
当网络请求完毕后,直接在网络线程(非UI线程)里调用了在Adapter中新增的自定义方法addData(List)更新数据,而addData(List)方法内更新换完数据后,通过Handler发送Message的策略调用Adapter的notifyDataSetChanged()方法通知更新。
这么一来,并不能保证Adapter的数据更新时,立马调用notifyDataSetChanged()通知ListView,这两个线程之间的时间差引起的数据不同步,导致ListView的layoutChildren()中访问Adapter的getCount()方法时,Adapter内已经是最新数据源,而ListView内的缓存数据Count仍是旧数据的Count,该问题最终原因终于浮出水面。
4、解决方案
1.在本例中,解决方案是:把addData(List)方法内更新数据的代码挪出来,和notifyDataSetChanged()方法一同放在Handler里,保证数据更新时及时通知ListView。
为了尽量避免该问题,以后编程尽量从如下几个方面检查自己的代码:
1.确保Adapter的数据更新后一定要调用notifyDataSetChanged()方法通知ListView2.数据更新和notifyDataSetChanged()放在UI线程内,且必须同步顺序执行,不可异步3.仔细检查确认getCount()方法返回值是否正确
2.知道了原因,改起来就好办多了,我将我的adapter类改为:
在CODE上查看代码片派生到我的代码片
private class DeviceAdapter extends BaseAdapter { private LayoutInflater inflater; private ArrayList<Device> devices; public DeviceAdapter() { inflater = LayoutInflater.from(mContext); } @SuppressWarnings("unchecked") public void setDeviceList(ArrayList<Device> list) { if (list != null) { devices = (ArrayList<Device>) list.clone(); notifyDataSetChanged(); } } public void clearDeviceList() { if (devices != null) { devices.clear(); } notifyDataSetChanged(); } @Override public int getCount() { return devices == null ? 0 : devices.size(); }
相对于原来,我做了两项改动:
1.将所有数据“完全”保存在adapter内部,即使有外部数据进入,也会用.clone()重新生成副本,保证了数据完全是由adapter维护的。
2.保证所有setDeviceList()/clearDeviceList()是从主线程里调用的,如何保证是从主线程中调用的呢:
a.调用Activity.runOnUIThread()方法;
b.使用Handler(其实这并不非常准确,因为Handler也可以运行在非UI线程);
c.使用AsyncTask。
- 关于Adapter的The content of the adapter has changed问题分析
- 关于Adapter的The content of the adapter has changed问题分析
- Android开发教程--关于Adapter的The content of the adapter has changed问题分析
- 关于Adapter的The content of the adapter has changed问题分析
- 关于Adapter的The content of the adapter has changed问题分析
- 关于Adapter的The content of the adapter has changed问题分析
- 关于Adapter的The content of the adapter has changed问题分析
- Adapter的The content of the adapter has changed问题分析
- 关于Adapter的The content of the adapter has changed but ListView did not receive a notification.问题分析
- Adapter报错:The content of the adapter has changed
- Adapter报错:The content of the adapter has changed
- Adapter报错:The content of the adapter has changed
- Adapter报错:The content of the adapter has changed
- Adapter报错:The content of the adapter has changed
- Adapter报错:The content of the adapter has changed
- Adapter报错:The content of the adapter has changed
- Adapter报错:The content of the adapter has changed
- Adapter报错:The content of the adapter has changed
- 在android 6.0以上无法获取READ_PHONE_STATE权限的SecurityException的问题
- android开发中常用快捷键
- Ubuntu下编译安装 OpenCV 3.1
- ORA-01403:no data found 解决办法
- The import javax.servlet.http.HttpServletRequest cannot be resolved
- 关于Adapter的The content of the adapter has changed问题分析
- Dalvik虚拟机垃圾收集机制
- Java/Android源码在线阅读网站
- 新界面, 软键盘的弹出问题
- 学习Discuz! X3.2记录:新建模板及修改logo
- ubuntu14.04安装搜狗拼音
- hdu1078 记忆化搜索
- 文章标题
- 与其坐而论道,不如起而行之