I have a custom ListView
where I display some weapons which are retrieved from a local database. I have a total of 88 rows, for each row a text and an image is set each time getView()
is called. The ListView
lags while scrolling fast and the garbage collector is going insane, deleting some 1M objects per second. I'm not getting why.
Before I post my Adapter
implementation, some explanation about how the Image is set. My Weapon class is simply a data holder with setters and getters. This is how names and images are getting set when the database gets created (yeah it might seem very strange, but all other solutions work even more slowly):
private Weapon buildWeapon(Cursor cursor) {
Weapon w = new Weapon();
w.setId(cursor.getLong(0));
w.setName(cursor.getString(1));
w.setImage(Constants.ALL_WEAPON_IMAGES[(int) cursor.getLong(0)-1]);
return w;
}
So I have an Array
containing all weapon images in the form of R.drawable.somegun
. The data structure is implemented such way that ID-1 always points to the right drawable reference in my Array
. The image field in the Weapon class is an Integer
. Now you have an idea how my getImage()
method works and below goes my Adapter
:
public class Weapon_Adapter extends BaseAdapter {
private List<Weapon> items;
private LayoutInflater inflater = null;
private WeaponHolder weaponHolder;
private Weapon wp;
static class WeaponHolder {
public TextView text;
public ImageView image;
}
// Context and all weapons of specified class are passed here
public Weapon_Adapter(List<Weapon> items, Context c) {
this.items = (List<Weapon>) items;
inflater = LayoutInflater.from(c);
Log.d("Adapter:", "Adapter created");
}
@Override
public int getCount() {
return items.size();
}
@Override
public Weapon getItem(int position) {
return items.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
wp = (Weapon) getItem(position);
if (convertView == null) {
convertView = inflater.inflate(R.layout.category_row, null);
weaponHolder = new WeaponHolder();
weaponHolder.text = (TextView) convertView
.findViewById(R.id.tvCatText);
weaponHolder.image = (ImageView) convertView
.findViewById(R.id.imgCatImage);
convertView.setTag(weaponHolder);
}
weaponHolder = (WeaponHolder) convertView.getTag();
weaponHolder.text.setText(wp.getName());
weaponHolder.image.setImageResource(wp.getImage());
// weaponHolder.image.setImageResource(R.drawable.ak74m);
return convertView;
}}
Now the strange thing: using the outcommented line to statically set the same image for all items removes all lags and GC in not even called once! I'm not getting it.. wp.getImage()
returns exactly the same thing, only R.drawable.name
is different for each weapon. But GC removes tons of objects and the ListView
lags while scrolling. Any ideas what I'm doing wrong?
UPDATE
I've moved setting images to an AsyncTask
and the lag is gone now:
public class AsyncImageSetter extends AsyncTask<Void, Void, Void> {
private ImageView img;
private int image_resId;
private Bitmap bmp;
private Context c;
public AsyncImageSetter(Context c, ImageView img, int image_ResId, Bitmap bmp) {
this.img = img;
this.image_resId = image_ResId;
this.bmp = bmp;
this.c = c;
}
@Override
protected Void doInBackground(Void... params) {
bmp = BitmapFactory.decodeResource(c.getResources(), image_resId);
return null;
}
@Override
protected void onPostExecute(Void result) {
img.setImageBitmap(bmp);
bmp = null;
super.onPostExecute(result);
}
}
However, GC is still called like crazy and RAM consumption increases when scrolling the whole List up and down. The question is now: how can I optimize image recycling to avoid RAM usage increase?