使用安卓ListView搭配Adapter实现列表时,经常会出现下面错误
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.
提示说adapter内容已经改变但是没有通知listview,这是怎么产生的呢?从源代码搜索Listview发现出错在这几行
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中由mAdapter.getCount()返回的及时list数组大小和listview中缓存的数组大小已经发生了冲突,所以导致异常抛出。
好了下面我们重现一下这个异常:
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:text="开始" android:id="@+id/main_TV"
android:background="#ff66f545" android:textSize="20dip"
android:padding="0dip"/>
<Button android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="button1" android:id="@+id/main_button"/>
<ListView android:layout_width="fill_parent" android:layout_height="fill_parent"
android:id="@+id/listview"/>
</LinearLayout>
代码:
public class HelloanActivity extends Activity implements View.OnClickListener
{
private TextView textView = null;
private ListView listView = null;
private Vector<String> vectorsStrings = new Vector<String>();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("HelloanActivity -> oncreate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
setContentView(R.layout.main);
textView = (TextView)findViewById(R.id.main_TV);
findViewById(R.id.main_button).setOnClickListener(this);
listView = (ListView)findViewById(R.id.listview);
vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");
vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");
vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");
vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");
vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");
vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");
vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");
vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");vectorsStrings.add("sfsfssfs");
listView.setAdapter(new MyAdapter());
}
public void onClick(View v)
{
switch (v.getId()) {
case R.id.main_button:
vectorsStrings.add("ssssssss");
break;
default:
break;
}
}
private class MyAdapter extends BaseAdapter
{
@Override
public int getCount() {
System.out.println("get Adapter Count");
return vectorsStrings.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(convertView == null)
{
convertView = new TextView(HelloanActivity.this);
}
((TextView)convertView).setText(vectorsStrings.elementAt(position));
return convertView;
}
}
}
重现的机制很简单,通过点击按钮模拟后台线程更改数组大小(注意在这过程中不要动listview),通过
System.out.println("get Adapter Count");
这句我们发现每一次手指滑动listview都会重新获取一下当前数组长度,而这个操作当然是在UI线程完成的。
现在我们不动listview,按一下按钮更改vector大小,再滑动一下listview,异常出现了,因为这时listview中缓存的大小mItemCount 已经和
public int getCount()
返回的大小冲突,导致crash。
再读一下异常的建议
Make sure the content of your adapter is not modified from a background thread, but only from the UI thread
因为将vector修改(假定为操作1)和adapter.notifyDataSetChanged()(假定为操作2)放在同一个UI线程时这是一个同步操作,所以总能保证public int getCount()(假定为操作3)不会在操作1和操作2之间发生,这就是为什么需要将修改vector值和操作2放在UI线程原因。
但是,当我们在实现一个动态列表时,例如需要从服务器不断更新列表内容,该怎么做?
两个办法,将http请求动作(或其他需要block的操作)放在后台线程,将结果的修改使用handler消息机制或者使用asyntask放到UI线程
你懂的~