1. 程式人生 > >ListView資料來源不同步問題The content of the adapter has changed but ListView did not receive a notification.

ListView資料來源不同步問題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 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一定要注意,修改資料來源後及時通知更新,否則一旦系統因為某種原因重繪介面,就會出現問題