今天做Android作业的时候发现个现象:
List<SongVO> mSongVOs = getSongs(A);//数据A
SongAdapter mSongAdapter = new SongAdapter(getApplicationContext(), mSongVOs);//用数据初始化适配器
lvMusicList.setAdapter(mSongAdapter);//为ListView绑定适配器
//情况1.可以刷新Listview数据
mSongVOs.remove(x);
mSongAdapter.notifyDataSetChanged();
//情况2.不刷新
mSongVOs=getSongs(B);//数据B
mSongAdapter.notifyDataSetChanged();
//情况3.可以刷新
mSongVOs.clear();
mSongVOs.addAll(helper.getAudios());
mSongAdapter.notifyDataSetChanged();
摸索了一下,找到了原因:
查看SongAdapter的构造函数:
public class SongAdapter extends BaseAdapter {
private List<SongVO> mData;
private Context mContext;
public SongAdapter(Context context, List<SongVO> songs) {
mContext = context;
mData = songs;
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
...
}
Adapter通常的写法都是用一个成员变量mData持有着对数据的引用,之后ListView的刷新都是通过Adapter.getItem()等方法进行更新,而这些方法访问的数据时这个mData指向的数据。如果这个数据不变,那么notifyDataSetChanged自然不会刷新。
在情况1、3中,局部变量mSongVOs和SongAdapter的成员变量mData指向的是同一块内存地址,即数据A,因此mSongVOs修改A后,notifyDataSetChanged通过mData再访问A时发现数据变了,所以就更新了ListView。
在情况2中,局部变量mSongVOs通过getSong(B)获取新数据B,实际是mSongVOs指向了内存中另外一块地址B,而此时mData指向的仍然是A。因此notifyDataSetChanged观察到A始终没有变化,因此不刷新。
所以,情况2,并不是Adapter“数据变了却不刷新”,而是“数据根本没变”。
而网上说重新对adapter绑定即可刷新,实际上就是重新绑定了成员变量mData,因此可以刷新。
情况2、3的业务逻辑是换了一整批数据,编程上情况2似乎是很直观和合理的,但是得注意了,得用第三种写法。
再次论内存分析的重要性!