0

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:

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!

Community
  • 1
  • 1
Balázs Édes
  • 13,452
  • 6
  • 54
  • 89
  • 1
    `SoftReference` does not work as a cache, the system does not know which of your references to gc and which to keep. http://developer.android.com/training/displaying-bitmaps/load-bitmap.html shows an approach using `LruCache` and https://github.com/nostra13/Android-Universal-Image-Loader does about the same – zapl Sep 07 '12 at 13:33

1 Answers1

1

I would stop using the Thread like you are and use an AsyncTask. Use this task to get the file from disk and place it in a cache (if it is not there). Return the cached value to the ImageView in your adapter.

So, in your adapter pass in the ImageView to the AsyncTask. Let the task do the work.

Nick
  • 1,340
  • 1
  • 15
  • 23