最近项目中出现了很多下面的异常:
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(16908298, class com.handmark.pulltorefresh.library.PullToRefreshListView$InternalListViewSDK9) with Adapter(class android.widget.HeaderViewListAdapter)]at android.widget.ListView.layoutChildren(ListView.java:1538)at
android.widget.ListView.setSelectionInt(ListView.java:1955)at android.widget.AbsListView.resurrectSelection(AbsListView.java:5269)at android.widget.AbsListView.onWindowFocusChanged(AbsListView.java:2792)at android.view.View.dispatchWindowFocusChanged(View.java:7325)at
android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:933)at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at
android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at
android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:937)at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:2890)at
android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loop(Looper.java:137)at android.app.ActivityThread.main(ActivityThread.java:4745)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:786)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)at dalvik.system.NativeStart.main(Native Method)
追踪该异常,发现其源头都来自于ListView.layoutChildren()方法
ListView比较了mItemCount和Adapter的getCount,发现两者不相等,抛出异常
这个mItemCount就是ListView内部缓存的一个子条目的个数,adapter的getCount就是我们继承Adapter重写的方法,返回数据源的个数
所以我们就知道:
当listView的layout.layoutChildren()方法被调用时,其内部缓存的条目个数和我们的数据源数量不一致时,会抛出此异常
通俗点就是,数据源变化没有及时通知到ListView
接下来看下代码:
list.addAll(data);
hideWaiting();
adapter.setData(list);
adapter.notifyDataSetChanged();
很简单的代码,改变数据源,然后setData,然后notifyDataSetChanged
表面看起来没有任何问题,但是请注意,第二行数据源修改后调用了hideWaiting方法,隐藏Dialog
这个操作就有可能触发系统重绘界面,一直调用到layouChildren(),出现问题,抛出异常
那么layoutChildren()的调用时机呢?
当界面发生任何改变时,如创建销毁,输入法弹出隐藏,显示隐藏Dialog,都会调用到该方法
此方法是由系统调用的,充满了不确定性,所以尽量还是及时通知更新数据
总结:
使用Adapter一定要注意,修改数据源后及时通知更新,否则一旦系统因为某种原因重绘界面,就会出现问题