关于Adapter的The content of the adapter has changed问题分析

 

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  
 

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、解决方案

在本例中,解决方案是:把addData(List)方法内更新数据的代码挪出来,和notifyDataSetChanged()方法一同放在Handler里,保证数据更新时及时通知ListView。
 
为了尽量避免该问题,以后编程尽量从如下几个方面检查自己的代码:
  • 确保Adapter的数据更新后一定要调用notifyDataSetChanged()方法通知ListView
  • 数据更新和notifyDataSetChanged()放在UI线程内,且必须同步顺序执行,不可异步
  • 仔细检查确认getCount()方法返回值是否正确

【原创】关于Adapter的The content of the adapter has changed问题分析的更多相关文章

  1. 【转】关于Adapter的The content of the adapter has changed问题分析 关于Adapter的The content of the adapter has changed问题分析

    原文网址:http://www.cnblogs.com/monodin/p/3874147.html 1.问题描述 1 07-28 17:22:02.162: E/AndroidRuntime(167 ...

  2. ListView:The content of the adapter has changed but ListView did not receive a notification终极解决方法

    使用ListView时遇到如下的异常信息: 10-26 18:30:45.085: E/AndroidRuntime(7323): java.lang.IllegalStateException: T ...

  3. The content of the adapter has changed but ListView did not receive a notification

    java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive ...

  4. Android The content of the adapter has changed but ListView did not receive a notification

    The content of the adapter has changed but ListView did not receive a notification. Make sure the co ...

  5. Android The content of the adapter has changed but ListView did not receive a notification终极解决方法

    这几天做一个自动扫描SD卡上所有APK文件的小工具,扫描过程中会把APK添加到LISTVIEW中显示,结果出现以下错误:(有时候触摸更新数据时候,触摸listview也会报错) E/AndroidRu ...

  6. Android报错:The content of the adapter has changed...与Channel is unrecoverably broken and will be disposed的分析与解决办法

    在Android中adapter错误: The content of the adapter has changed but ListView did not receive a notificati ...

  7. 【转】解决java.lang.IllegalStateException: The content of the adapter has changed but ListView...的问题

    原文网址:http://blog.csdn.net/ueryueryuery/article/details/20607845 我写了一个Dialog,Dialog中有一个ListView,想要点Li ...

  8. Android开发-- The content of the adapter has changed but ListView did not receive a notification - With AsyncTask

    最近在联系开发DaysMatter时遇到一个问题: app中使用ListView来展示所有事件,每次添加完事件后使用下面代码来更新ListView. toDoListView.refreshDrawa ...

  9. 问题解决:The content of the adapter has changed but ListView did not receive a notification

    1. 不要在后台线程中直接调用adapter 2. 不要在后台线程中修改adapter绑定的数据 如果对adapter或者adapter绑定的数据是在线程中,加上runOnUiThread就可以了 r ...

随机推荐

  1. 容器适配器之priority_queue

    template <class T, class Container = vector<T>,                class Compare = less<type ...

  2. vbs操作txt文本文件常用方法(函数)

    创建文件 dim fso, f set fso = server.CreateObject("Scripting.FileSystemObject") set f = fso.Cr ...

  3. 针对《来用》的NABC分析

    项目名:<来用> 特点:拥有以往win7在内的众多小游戏 NABC分析 N(need需求): 之所以有这个想法是因为,在WIN7,XP系统中往往有很多众所周知的小游戏(比如扫雷),但是在w ...

  4. 水王ID查找

    一. 题目 1 三人行设计了一个灌水论坛.信息学院的学生都喜欢在上面交流灌水,传说在论坛上有一个“水王”,他不但喜欢发帖,还会回复其他ID发的每个帖子.坊间风闻该“水王”发帖数目超过了帖子数目的一半. ...

  5. 向Array中添加希尔排序

    希尔排序思路 我们在第 i 次时取gap = n/(2的i次方),然后将数组分为gap组(从下标0开始,每相邻的gap个元素为一组),接下来我们对每一组进行直接插入排序. 希尔排序实现 Functio ...

  6. asp.net动态添加GridView的模板列,并获取列值

    一.动态添加模板列: 1.建立模板列样式: 说明:下边代码可以直接写在aspx文件中,也可以单独建立cs文件:另外,我没有写button.linkButton等控件,意思差不多,不过当需要添加事件时, ...

  7. Python 读写excel数据

    读取excel 文件的数据 import csv with open('D:/mystuff/11.csv','r') as f: reader = csv.reader(f) for row in ...

  8. protobuf的安装和使用

    以下全部基于win7系统. protobuf是什么,有什么用网上说的已经很多了.这里就是说一下怎么使用.就当给自己做个笔记吧. .proto文件的语法什么的也请网上查看,挺多的. 第一步: 下载pro ...

  9. 用U盘作为启动盘,安装Yosemite

    1.选择一个存贮空间大于Yosemite系统(Yosemite系统大概5.1G左右)的U盘,用磁盘工具(Disk Utility)对此磁盘进行分区,可以参照此链接处对U盘进行分区.如果用磁盘工具进行分 ...

  10. CSRF(跨站请求伪造)攻击方式

    一.CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSR ...