views:

101

answers:

2

Hi, I'm about to create a lazy image loader for images both from device and from the web for a ListView. I'm thinking of what to use and how, on one hand i can use a thread that pools my request (always running, and i can attach a view and an adapter and it will handle the image loading for me), cache my allready loaded images and checks for the visibility of the images before i load so i wont do unneeded job.

I had another thought of using the AsyncTask like many suggest in the forum. Terhe is one drawback though.I see that many use new MyTask().execute(urls); this presents a problem if i want to start loading and stop loading images on demand. If i use the async task per image then i need new async task for each image, that's a lot of 'new' to make, i can use a pool, but if too many async tasks are stuck, i will still create about 150-200 asyc tasks, too many for my taste...

What do you guys think ? i think a thread will do a nicer job here: 1. keep on running till killed 2. try to get a job from the queue, if not jobs, wait. 3. if a job is available, get it and start processing. 4. each request is processed alone,serially and blocks the thread. 5. once does goes on with '2'. 6. each enqueue that is done by the adapter using startLoadingImage() for views that need to be displayed will create anew job and call notify on the wait lock. I can optimize this code with a pool of threads if i want several GET\POST requests in parallel. Also I'm caching the images i already downloaded \ loaded for fast load on next access. the idea is to minimize GC and list's slagging.

A: 

I think you're doing premature optimization. Just implement what you need in fastest way possible, you'll always can improve implementation afterwards. Also why would you need to start 200 AsyncTasks at the same time? I do not think that you're going to show all the images at one screen (and in case of ListView, why load all the images even though user can never scroll to the end of the list?).

Konstantin Burov
no, thats the point the user CAN scroll to the end, all images will be preloaded with a default image like "wait loading" or the like, once the image is loaded the view is notified and the image is placed in the right view. once it's cached response time for loading is fast so no need to do it in an asynchronously.Regarding the 200 async tasks, it was just an example of why i don't\can't use it...
codeScriber
A: 

I implemented something like this:

/** Contains all the pending requests for thumbnails. */
private LinkedList<Uri> mPendingThumbnailRequests = new LinkedList<Uri>();

private ThumbnailGetter mThmGetter = null;
/**
 * Asynchronous process for retrieving thumbnails from Uris.
 */
private class ThumbnailGetter extends AsyncTask<Uri, Integer, Uri> {
    private final String LOG_TAG = ThumbnailGetter.class
            .getSimpleName();
    /** The Uri beeing processed */
    private Uri mUri = null;
    /*
     * (non-Javadoc)
     * 
     * @see android.os.AsyncTask#doInBackground(Params[])
     */
    @Override
    protected Uri doInBackground(Uri... uris) {
        // We process Uris one after another... so the Array contains
        // only one Uri.
        mUri = uris[0];
        // Let the ThumbnailLoader do the job.
        Uri result = ItemsLoader.getThumbnail(mContext, mUri);
        return result;
    }
    /*
     * (non-Javadoc)
     * 
     * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
     */
    @Override
    protected void onPostExecute(Uri result) {
        super.onPostExecute(result);
        // Give the retrieved thumbnail to the adapter...
        mImageAdapter.updateThumbUri(mUri, result);
        // then process any other pending thumbnail request.
        if (!mPendingThumbnailRequests.isEmpty()) {
            mThmGetter = new ThumbnailGetter();
            mThmGetter.execute(mPendingThumbnailRequests.poll());
         }
     }
}

I then add Uris to load using:

if (!mPendingThumbnailRequests.contains(imageUri)) {
    mPendingThumbnailRequests.offer(imageUri);
    if (mThmGetter == null
            || mThmGetter.getStatus() == AsyncTask.Status.FINISHED) {
        // If the previous instance of the thumbnail getter has
        // finished, start a new one.
        mHandler.sendEmptyMessage(MSG_SHOW_INDETERMINATE_PROGRESS);
        mThmGetter = new ThumbnailGetter();
        mThmGetter.execute(mPendingThumbnailRequests.poll());
    }
}

This even let you cancel requests using mPendingThumbnailRequests.remove()

The full implementation is here: http://code.google.com/p/emailalbum/source/browse/EmailAlbumAndroid/trunk/src/com/kg/emailalbum/mobile/creator/SelectPictures.java

Kevin Gaudin
This code is definitely nice , but if i consider a list with images that loads them in unknown speed and batches (since user can scroll as fast or slow as he likes) u'll get RELATIVLY many AyncTasks created as opposed to your own thread that is created once and sleeps till you enqueue a new object, true it's a bit more memory for him, but it's redundant as opposed to the GC prevented if you use your own pool and avoid creating new objects in the run() functions.
codeScriber
There won't be many AsyncTasks created as we reuse the existing one if there is one. Well, actually it recreates a new one when the user starts scrolling and there is some thumbnails to retrieve... but this is just a matter of one allocation at the very beginning of the scrolling. I have never been fond of sleeping Threads and worries more about those Threads never being stoped or being duplicated.
Kevin Gaudin
Hey, I just figured out that you are right, I create a new AsyncTask for each Uri... I'll work on this. Thanks for showing me this. ;-)
Kevin Gaudin