I have a listview that probably has infinite items loaded on scrolling infinitely.
Each item in list view has one or two images which I'm lazy loading.
Everything works great but when I scroll for really long it crashes with this in log cat
08-07 15:26:25.231: E/AndroidRuntime(30979): FATAL EXCEPTION: Thread-60
08-07 15:26:25.231: E/AndroidRuntime(30979): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
08-07 15:26:25.231: E/AndroidRuntime(30979): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
08-07 15:26:25.231: E/AndroidRuntime(30979): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:493)
08-07 15:26:25.231: E/AndroidRuntime(30979): at com.test.android.helper.LazyImageLoader.decodeFile(LazyImageLoader.java:171)
08-07 15:26:25.231: E/AndroidRuntime(30979): at com.test.android.helper.LazyImageLoader.getBitmap(LazyImageLoader.java:112)
08-07 15:26:25.231: E/AndroidRuntime(30979): at com.test.android.helper.LazyImageLoader.access$2(LazyImageLoader.java:106)
08-07 15:26:25.231: E/AndroidRuntime(30979): at com.test.android.helper.LazyImageLoader$ImageLoader.run(LazyImageLoader.java:197)
In my lazy image loader I am storing bitmaps in a WeakHashMap
. So garbage collector should collect the bitmaps right?
My lazy imageloader works something like this.
I call displayImage()
from my adapter with url and a reference to imageview
public void displayImage(String url, ImageView imageView, int defaultImageResourceId){
latestImageMetaData.put(imageView, url);
if(weakhashmapcache.containsKey(url)){
imageView.setImageBitmap(weakhashmapcache.get(url));
}
else{
enqueueImage(url, imageView, defaultImageResourceId);
imageView.setImageResource(defaultImageResourceId);
}
}
So if I find the image in cache, I set it directly, otherwise I queue it with function enqueueImage()
.
private void enqueueImage(String url, ImageView imageView, int defaultImageResourceId){ Image image = new Image(url, imageView, defaultImageResourceId); downloadqueue.add(image); // downloadQueue is a blocking queue which waits for images to be added //If the queue is about to get full then delete the elements that are ahead in the queue as they are anyway not visible Iterator iterator = downloadQueue.iterator(); while(iterator.hasNext() && downloadQueue.remainingCapacity() < 80){ downloadQueue.remove(iterator.next()); } }
And my image loader thread is this -
class ImageLoader extends Thread {
public void run() {
Image firstImageInQueue;
try {
while((firstImageInQueue = downloadQueue.take()) != SHUTDOWN_TOKEN)
{
Bitmap imageBitmap = getBitmap(firstImageInQueue.url);
if(imageBitmap != null){
weakhashmap.put(firstImageInQueue.url, imageBitmap);
BitmapDisplayer displayer = new BitmapDisplayer(imageBitmap, firstImageInQueue.imageView, firstImageInQueue.url, firstImageInQueue.defaultImageResourceId);
Activity activity = (Activity)firstImageInQueue.imageView.getContext();
activity.runOnUiThread(displayer);
}
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
finally {
imageLoaderTerminated = true;
}
}
}
getBitmap()
just fetches image from url scales and decodes it into a Bitmap object. BitmapDisplayer
is just a Runnable which does the setting of image to imageview on UI thread.
What am I doing wrong?