Android ListView cotent of the adapter has changed but ListView did not receive a notification

前段时间完成某项目离线数据部分时,用了大量ListView显示加载数据,在碰到大数据量的加载时容易出现的错误。即我们将List作为参数传递给ListView的Adapter,同时在开辟线程加载数据时直接对刚刚参数传递的List操作,具体代码如下:

package com.jackchan.listtest;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class ListTestActivity extends Activity {
	
	private ListView mListView;
	private Button mBtn;
	private int text = 0;
	private List<String> mList;
	private MyAdapter mAdapter;
	private List<String> mListBak;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mListView = (ListView)findViewById(R.id.listview);
        mBtn = (Button)findViewById(R.id.btn);
        mList = new ArrayList<String>();
  //      mListBak = new ArrayList<String>();
        mAdapter = new MyAdapter(mList);
        mListView.setAdapter(mAdapter);
        mBtn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				GetDataTask task = new GetDataTask();
				task.execute(new Void[0]);
			}
		});
    }
    
    private class GetDataTask extends AsyncTask<Void, Void, Void>{

		@Override
		protected Void doInBackground(Void... params) {
		//	mListBak.clear();
			mList.clear();
			for(int i = 0; i < 50; i++){
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			//	mListBak.add(text+"");
				mList.add(text+"");
				text++;
			}
			return null;
		}

		@Override
		protected void onPostExecute(Void result) {
			super.onPostExecute(result);
		//	mAdapter.addList(mListBak);
			mAdapter.notifyDataSetChanged();
		}
    	
		
    }
    
    private class MyAdapter extends BaseAdapter{
    	
    	private TextView mItem;
    	private List<String> mData;
    	
    	
		public MyAdapter(List<String> data) {
			this.mData = data;
		}

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return mData.size();
		}

		@Override
		public Object getItem(int arg0) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			if(convertView == null){
				convertView = LayoutInflater.from(ListTestActivity.this).inflate(R.layout.item, null);
				mItem = (TextView)convertView.findViewById(R.id.item);
				convertView.setTag(mItem);
			}
			else{
				mItem = (TextView)convertView.getTag();
			}
			mItem.setText(mData.get(position));
			return convertView;
		} 
    	
		public void addList(List list){
			mData.addAll(list);
		}
    }
}

这段代码在执行时,如果你在GetDataTask执行doInBackground的时候,去滑动屏幕的ListView,很容易出现

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. 

的系统崩溃错误,这段话的意思就是说适配器的内容发生变化,但ListView没有收到通知,确保适配器的内容只在UI线程更新不要再后台线程更新。

而我们的程序无论是在UI线程还是异步GetDataTask里都是直接对适配器内容mList进行操作,所以会出现错误。

 

规避以上错误的具体方法是:

1、重新分配一个List对象专门在异步任务里加载数据;

2、在异步任务结束后在UI线程把新分配的List的内容添加到适配器的list里;

3、在UI线程里适配器执行notifyDataSetChanged()更新数据

 

上面的代码更改如下

package com.jackchan.listtest;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class ListTestActivity extends Activity {
	
	private ListView mListView;
	private Button mBtn;
	private int text = 0;
	private List<String> mList;
	private MyAdapter mAdapter;
	private List<String> mListBak;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mListView = (ListView)findViewById(R.id.listview);
        mBtn = (Button)findViewById(R.id.btn);
        mList = new ArrayList<String>();
        mListBak = new ArrayList<String>();
        mAdapter = new MyAdapter(mList);
        mListView.setAdapter(mAdapter);
        mBtn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				GetDataTask task = new GetDataTask();
				task.execute(new Void[0]);
			}
		});
    }
    
    private class GetDataTask extends AsyncTask<Void, Void, Void>{

		@Override
		protected Void doInBackground(Void... params) {
			mListBak.clear();
		//	mList.clear();
			for(int i = 0; i < 50; i++){
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				mListBak.add(text+"");
			//	mList.add(text+"");
				text++;
			}
			return null;
		}

		@Override
		protected void onPostExecute(Void result) {
			super.onPostExecute(result);
			mAdapter.addList(mListBak);
			mAdapter.notifyDataSetChanged();
		}
    	
		
    }
    
    private class MyAdapter extends BaseAdapter{
    	
    	private TextView mItem;
    	private List<String> mData;
    	
    	
		public MyAdapter(List<String> data) {
			this.mData = data;
		}

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return mData.size();
		}

		@Override
		public Object getItem(int arg0) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			if(convertView == null){
				convertView = LayoutInflater.from(ListTestActivity.this).inflate(R.layout.item, null);
				mItem = (TextView)convertView.findViewById(R.id.item);
				convertView.setTag(mItem);
			}
			else{
				mItem = (TextView)convertView.getTag();
			}
			mItem.setText(mData.get(position));
			return convertView;
		} 
    	
		public void addList(List list){
			mData.addAll(list);
		}
    }
}


你可能感兴趣的:(Android ListView cotent of the adapter has changed but ListView did not receive a notification)