package com.ch_linghu.fanfoudroid.app; import java.lang.Thread.State; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import com.ch_linghu.fanfoudroid.TwitterApplication; import com.ch_linghu.fanfoudroid.http.HttpException; public class LazyImageLoader { private static final String TAG = "ProfileImageCacheManager"; public static final int HANDLER_MESSAGE_ID = 1; public static final String EXTRA_BITMAP = "extra_bitmap"; public static final String EXTRA_IMAGE_URL = "extra_image_url"; private ImageManager mImageManager = new ImageManager( TwitterApplication.mContext); private BlockingQueue<String> mUrlList = new ArrayBlockingQueue<String>(50); private CallbackManager mCallbackManager = new CallbackManager(); private GetImageTask mTask = new GetImageTask(); /** * 取图片, 可能直接从cache中返回, 或下载图片后返回 * * @param url * @param callback * @return */ public Bitmap get(String url, ImageLoaderCallback callback) { Bitmap bitmap = ImageCache.mDefaultBitmap; if (mImageManager.isContains(url)) { bitmap = mImageManager.get(url); } else { // bitmap不存在,启动Task进行下载 mCallbackManager.put(url, callback); startDownloadThread(url); } return bitmap; } private void startDownloadThread(String url) { if (url != null) { addUrlToDownloadQueue(url); } // Start Thread State state = mTask.getState(); if (Thread.State.NEW == state) { mTask.start(); // first start } else if (Thread.State.TERMINATED == state) { mTask = new GetImageTask(); // restart mTask.start(); } } private void addUrlToDownloadQueue(String url) { if (!mUrlList.contains(url)) { try { mUrlList.put(url); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // Low-level interface to get ImageManager public ImageManager getImageManager() { return mImageManager; } private class GetImageTask extends Thread { private volatile boolean mTaskTerminated = false; private static final int TIMEOUT = 3 * 60; private boolean isPermanent = true; @Override public void run() { try { while (!mTaskTerminated) { String url; if (isPermanent) { url = mUrlList.take(); } else { url = mUrlList.poll(TIMEOUT, TimeUnit.SECONDS); // waiting if (null == url) { break; } // no more, shutdown } // Bitmap bitmap = ImageCache.mDefaultBitmap; final Bitmap bitmap = mImageManager.safeGet(url); // use handler to process callback final Message m = handler.obtainMessage(HANDLER_MESSAGE_ID); Bundle bundle = m.getData(); bundle.putString(EXTRA_IMAGE_URL, url); bundle.putParcelable(EXTRA_BITMAP, bitmap); handler.sendMessage(m); } } catch (HttpException ioe) { Log.e(TAG, "Get Image failed, " + ioe.getMessage()); } catch (InterruptedException e) { Log.w(TAG, e.getMessage()); } finally { Log.v(TAG, "Get image task terminated."); mTaskTerminated = true; } } @SuppressWarnings("unused") public boolean isPermanent() { return isPermanent; } @SuppressWarnings("unused") public void setPermanent(boolean isPermanent) { this.isPermanent = isPermanent; } @SuppressWarnings("unused") public void shutDown() throws InterruptedException { mTaskTerminated = true; } } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case HANDLER_MESSAGE_ID: final Bundle bundle = msg.getData(); String url = bundle.getString(EXTRA_IMAGE_URL); Bitmap bitmap = (Bitmap) (bundle.get(EXTRA_BITMAP)); // callback mCallbackManager.call(url, bitmap); break; default: // do nothing. } } }; public interface ImageLoaderCallback { void refresh(String url, Bitmap bitmap); } public static class CallbackManager { private static final String TAG = "CallbackManager"; private ConcurrentHashMap<String, List<ImageLoaderCallback>> mCallbackMap; public CallbackManager() { mCallbackMap = new ConcurrentHashMap<String, List<ImageLoaderCallback>>(); } public void put(String url, ImageLoaderCallback callback) { Log.v(TAG, "url=" + url); if (!mCallbackMap.containsKey(url)) { Log.v(TAG, "url does not exist, add list to map"); mCallbackMap.put(url, new ArrayList<ImageLoaderCallback>()); // mCallbackMap.put(url, Collections.synchronizedList(new // ArrayList<ImageLoaderCallback>())); } mCallbackMap.get(url).add(callback); Log.v(TAG, "Add callback to list, count(url)=" + mCallbackMap.get(url).size()); } public void call(String url, Bitmap bitmap) { Log.v(TAG, "call url=" + url); List<ImageLoaderCallback> callbackList = mCallbackMap.get(url); if (callbackList == null) { // FIXME: 有时会到达这里,原因我还没想明白 Log.e(TAG, "callbackList=null"); return; } for (ImageLoaderCallback callback : callbackList) { if (callback != null) { callback.refresh(url, bitmap); } } callbackList.clear(); mCallbackMap.remove(url); } } }