关于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. Unity3D定制新建C#文件的头描述

    1. 修改模板内容如下: MAC:Unity.app/Contents/Resources/ScriptTemplates/81-C# Script-NewBehaviourScript.cs.txt ...

  2. U盘安装 Windows XP 原版 ISO 的几点心得

    虽然我一直致力于推动最新操作系统的部署,劝说周围朋友尽快淘汰 Windows XP,但还是难免有一些老电脑.老朋友的电脑,坚持要使用 XP 系统. 这里有几点使用U盘安装 Windows XP 原版 ...

  3. 电梯调度--c++--软件工程

    一.设计思路 (1)将乘客要去的楼层数存起来. (2)假设yi为乘客要爬楼层数之和,yi=n1*|(n1-ni)|+n2*|(n2-ni)|+..+n18*|(n18-ni)| (3)比较y1到y18 ...

  4. MySQL 分组

    MySQL GROUP BY 语句 GROUP BY 语句根据一个或多个列对结果集进行分组. 在分组的列上我们可以使用 COUNT, SUM, AVG,等函数. GROUP BY 语法 SELECT ...

  5. POSⅨ thread

    POSⅨ thread 简称为pthread,Posix线程是一个POSⅨ标准线程.该标准定义 内部API创建和操纵线程. 编辑本段作用 线程库实行了POSIX线程标准通常称为pthreads.POS ...

  6. poj 2342 Anniversary party

    题目链接:http://poj.org/problem?id=2342 题意:读题很容易懂,这里不做介绍. 解法:树形DP之路的第一道题. #include<iostream> #incl ...

  7. File not found images\Thumbs.db.

    启动eclipse正常,部署项目的时候发生这个错误. 解决办法: 1. 从tomcat中删除这个项目,并清空work目录下的缓存 2. 在eclipse中执行project>Clean>C ...

  8. [翻译]The Neophyte's Guide to Scala Part 12: Type Classes

    The Neophyte's Guide to Scala Part 12: Type Classes 过去的两周我们讨论了一些使我们保持DRY和灵活性的函数式编程技术,特别是函数组合,partial ...

  9. OpenLayers3 online build

    openlayers3使用了一个比较复杂的build工具,从github上下载下来的代码中并没有build之后的版本,要配置build环境又比较繁琐,好在官方的example中提供了在线的版本,下面就 ...

  10. mysql触发器使用实例

    DELIMITER $$ USE `db`$$ DROP TRIGGER `member_walletinit_trigger`$$ CREATE TRIGGER `member_walletinit ...