Before making this question, I have searched and read these ones: Lazy load of images in ListView Android - Issue with lazy loading images into a ListView
My problem is I have a ListView, where:
- Each row contains an ImageView, whose content is to be loaded from the internet
- Each row's view is recycled as in ApiDemo's List14
What I want ultimately:
- Load images lazily, only when the user scrolls to them
- Load images on different thread(s) to maintain responsiveness
My current approach:
- In the adapter's getView()method, apart from setting up other child views, I launch a new thread that loads the Bitmap from the internet. When that loading thread finishes, it returns theBitmapto be set on theImageView(I do this usingAsyncTaskorHandler).
- Because I recycle ImageViews, it may be the case that I first want to set a view withBitmap#1, then later want to set it toBitmap#2when the user scrolls down.Bitmap#1may happen to take longer thanBitmap#2to load, so it may end up overwritingBitmap#2on the view. I solve this by maintaining aWeakHashMapthat remembers the lastBitmapI want to set for that view.
Below is somewhat a pseudocode for my current approach. I've ommitted other details like caching, just to keep the thing clear.
public class ImageLoader {
    // keeps track of the last Bitmap we want to set for this ImageView
    private static final WeakHashMap<ImageView, AsyncTask> assignments
                                    = new WeakHashMap<ImageView, AsyncTask>();
    /** Asynchronously sets an ImageView to some Bitmap loaded from the internet */
    public static void setImageAsync(final ImageView imageView, final String imageUrl) {
        // cancel whatever previous task
        AsyncTask oldTask = assignments.get(imageView);
        if (oldTask != null) {
            oldTask.cancel(true);
        }
        // prepare to launch a new task to load this new image
        AsyncTask<String, Integer, Bitmap> newTask = new AsyncTask<String, Integer, Bitmap>() {
            protected void onPreExecute() {
                // set ImageView to some "loading..." image
            }
            protected Bitmap doInBackground(String... urls) {
                return loadFromInternet(imageUrl);
            }
            protected void onPostExecute(Bitmap bitmap) {
                // set Bitmap if successfully loaded, or an "error" image
                if (bitmap != null) {
                    imageView.setImageBitmap(bitmap);
                } else {
                    imageView.setImageResource(R.drawable.error);
                }
            }
        };
        newTask.execute();
        // mark this as the latest Bitmap we want to set for this ImageView
        assignments.put(imageView, newTask);
    }
    /** returns (Bitmap on success | null on error) */
    private Bitmap loadFromInternet(String imageUrl) {}
}
Problem I still have: what if the Activity gets destroyed while some images are still loading?
- Is there any risk when the loading thread calls back to the ImageView later, when the Activity is already destroyed?
- Moreover, AsyncTaskhas some global thread-pool underneath, so if lengthy tasks are not canceled when they're not needed anymore, I may end up wasting time loading things users
 don't see. My current design of keeping this thing globally is too ugly, and may eventually cause some leaks that are beyond my understanding. Instead of makingImageLoadera singleton like this, I'm thinking of actually creating separateImageLoaderobjects for differentActivities, then when anActivitygets destroyed, all itsAsyncTaskwill be canceled. Is this too awkward?
Anyway, I wonder if there is a safe and standard way of doing this in Android. In addition, I don't know iPhone but is there a similar problem there and do they have a standard way to do this kind of task?
Many thanks.
 
     
    