前段时间完成某项目离线数据部分时,用了大量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);
}
}
}