1胸懈、問題描述
在使用Listview給Adapter填充數(shù)據(jù)的時候,在Mi max手機上下拉刷新的時候崩了恰响,當時只有這個手機遇到這個問題趣钱,其他手機都OK,log大概如下;
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.
2胚宦、復(fù)現(xiàn)場景
使用ListView和Adapter實現(xiàn)動態(tài)增刪數(shù)據(jù)列表功能首有,初始化數(shù)據(jù)分為兩部分:本地和網(wǎng)絡(luò)。所以在Adapter的數(shù)據(jù)初始化的時候枢劝,先講本地數(shù)據(jù)添加到了容器內(nèi)井联。同時發(fā)起網(wǎng)絡(luò)請求,等加載完畢后追加到容器內(nèi)您旁。
問題出現(xiàn)在:當網(wǎng)絡(luò)請求完畢后追加數(shù)據(jù)的時候烙常,拋出上述異常。
3被冒、原因分析
Exception解讀:
Adapter的數(shù)據(jù)內(nèi)容已經(jīng)改變军掂,但是ListView卻未接收到通知轮蜕。要確保不在后臺線程中修改Adapter的數(shù)據(jù)內(nèi)容,而要在UI Thread中修改蝗锥。確保Adapter的數(shù)據(jù)內(nèi)容改變時一定要調(diào)用notifyDataSetChanged()方法跃洛。
當ListView緩存的數(shù)據(jù)Count和ListView中Adapter.getCount()不等時,會拋出該異常终议。
結(jié)合開頭的異常解讀汇竭,可以斷定肯定是Adapter數(shù)據(jù)動態(tài)更新的問題。仔細檢查了自己的代碼:
-我的情況
下拉的時候會去請求數(shù)據(jù)穴张,在網(wǎng)絡(luò)線程(非UI線程)然后判斷pageNum是否等于1细燎,如果等于1的話,list.clear(),然后把這個請求到的數(shù)據(jù)添加到list里面皂甘,但是這個處理是在runOnUiThread中玻驻,我分析的原因是,clear操作在子線程還未處理的時候偿枕,先執(zhí)行了list.add(),造成越界璧瞬。后來把處理都移到runOnUiThread中,如下:
runOnUiThread(new Runnable() {
@Override
public void run() {
if (pageNum == 1) {
infoList.clear();
}
infoList.addAll(reList);
adapter.setData(infoList);
adapter.notifyDataSetChanged();
}
});
-其他情況
當網(wǎng)絡(luò)請求完畢后渐夸,直接在網(wǎng)絡(luò)線程(非UI線程)里調(diào)用了在Adapter中新增的自定義方法addData(List)更新數(shù)據(jù)嗤锉,而addData(List)方法內(nèi)更新?lián)Q完數(shù)據(jù)后,通過Handler發(fā)送Message的策略調(diào)用Adapter的notifyDataSetChanged()方法通知更新墓塌。
這么一來瘟忱,并不能保證Adapter的數(shù)據(jù)更新時,立馬調(diào)用notifyDataSetChanged()通知ListView苫幢,這兩個線程之間的時間差引起的數(shù)據(jù)不同步访诱,導(dǎo)致ListView的layoutChildren()中訪問Adapter的getCount()方法時,Adapter內(nèi)已經(jīng)是最新數(shù)據(jù)源态坦,而ListView內(nèi)的緩存數(shù)據(jù)Count仍是舊數(shù)據(jù)的Count盐数,該問題最終原因終于浮出水面。
歸根結(jié)底都是一個原因伞梯。
4玫氢、解決方案
在本例中,解決方案是:把addData(List)方法內(nèi)更新數(shù)據(jù)的代碼挪出來谜诫,和notifyDataSetChanged()方法一同放在Handler里漾峡,保證數(shù)據(jù)更新時及時通知ListView。
為了盡量避免該問題喻旷,以后編程盡量從如下幾個方面檢查自己的代碼:
確保Adapter的數(shù)據(jù)更新后一定要調(diào)用notifyDataSetChanged()方法通知ListView
數(shù)據(jù)更新和notifyDataSetChanged()放在UI線程內(nèi)生逸,且必須同步順序執(zhí)行,不可異步
仔細檢查確認getCount()方法返回值是否正確