I am having a really hard time implementing a lazy loading list. I know, that there are countless of posts about similar issues, but please take a look at my problem, before judging.
The goal: Load thumbnails of Bitmaps present on the SD card, but not blocking the main (UI) thread, so the list would scroll smoothly.
Sources: The base of what i tried were these two posts:
- ListView is very laggy - Android (ViewHolder idea)
- Lazy load of images in ListView (The threading idea)
My attempts so far:
I have tried to implement an class what would load and cache (i don't know, if it is the appropriate expression) the thumbnails. It looks something like this (I'm try not to post a wall of code, so i cut the unimportant parts):
public class ThumbnailContainer
{
//this will store the thumbnails
private final HashMap<File, SoftReference<Bitmap>> data;
//this Handler class will update the ui, when we got the thumb from a thread
private static final class BitmapHandler extends Handler
{
private final WeakReference<ImageView> image;
public BitmapHandler(ImageView image)
{this.image = new WeakReference<ImageView>(image);}
@Override
public void handleMessage(Message msg)
{
if(image.get()!=null)
image.get().setImageBitmap((Bitmap) msg.obj);
}
}
public ThumbnailContainer(Context context)
{
data = new HashMap<File, SoftReference<Bitmap>>();
}
//this will set the Bitmap to the ImageView (load on a thread if required)
public void setBitmapOnThread(final File file, final ImageView view)
{
//contains will return true, if the data map contains the file key
//and the SoftReference is still vaild.
if (contains(file))
{
view.setImageBitmap(data.get(file).get());
return;
}
else
{
final Handler handler = new BitmapHandler(view);
final Thread thread = new Thread()
{
@Override
public void run()
{
Bitmap bitmap = getMeasuredBitmap(file);
Message msg = handler.obtainMessage(0, bitmap);
handler.sendMessage(msg);
}
};
thread.start();
}
}
// load the Bitmap if it isn't already, scales it down, and recycles the original
private Bitmap getMeasuredBitmap(File file)
{
if (contains(file))
return data.get(file).get();
else
{
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
/*counting the scale of the new Bitmap, i cut the detail*/
Bitmap measured = Bitmap.createScaledBitmap(bitmap, w, h, false);
bitmap.recycle();
data.put(file, new SoftReference<Bitmap>(measured));
return measured;
}
}
//returns true, if the map contains this file, and the reference is still valid
private boolean contains(File file)
{
if (data.containsKey(file))
{
if (data.get(file).get() != null) return true;
else return false;
}
return false;
}
}
The result: I still get a very laggy list-scrolling. It's like i haven't even added the threading solution, just loaded the thumbnails in the listadapters getView()
method. I have tried setting the Threads
priority (see setBitmapOnThread()
) to LOW
, and when i do this, the scrolling is usually smooth, and i can see the thumbnails loading, but when i scroll really fast, then i run out of memory. I assume that this is because too many threads are started, and they could not finish.
My question: Do you guys see an obvious error here?
If not, then would it be wise to stick with the low priority thread solution? If so, then is there a way to limit the thread count to a fixed number (like 5-6), and stop and join, the unfinished, before starting a new, if the max thread count is reached? I read about ThreadPools, but i never used one.
I would really appreciate any help!