I'm trying 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 already 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. There 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 开发者_Go百科will do a nicer job here:
- Keep on running till killed
- Try to get a job from the queue, if not jobs, wait.
- If a job is available, get it and start processing.
- Each request is processed alone,serially and blocks the thread.
- Once does goes on with '2'.
- 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 lists lagging.
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
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?).
精彩评论