package com.withiter.quhao.util.tool; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import com.withiter.quhao.QHClientApplication; import com.withiter.quhao.util.StringUtils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.ViewGroup.LayoutParams; import android.widget.ImageView; import android.widget.ImageView.ScaleType; public class AsynImageLoader { private static final String TAG = "AsynImageLoader"; private static AsynImageLoader asynImageLoader; // 缓存下载过的图片的Map // 任务队列 private List<Task> taskQueue; private boolean isRunning = false; /** 用于Chche内容的存储 */ private Hashtable<String, BtimapRef> bitmapRefs; /** 垃圾Reference的队列(所引用的对象已经被回收,则将该引用存入队列中) */ private ReferenceQueue<Bitmap> q; /** * 继承SoftReference,使得每一个实例都具有可识别的标识。 */ private class BtimapRef extends SoftReference<Bitmap> { private String _key = ""; public BtimapRef(Bitmap bmp, ReferenceQueue<Bitmap> q, String key) { super(bmp, q); _key = key; } } private AsynImageLoader() { // 初始化变量 bitmapRefs = new Hashtable<String, BtimapRef>(); q = new ReferenceQueue<Bitmap>(); taskQueue = new ArrayList<AsynImageLoader.Task>(); // 启动图片下载线程 isRunning = true; new Thread(runnable).start(); } /** * 取得缓存器实例 */ public static AsynImageLoader getInstance() { if (asynImageLoader == null) { asynImageLoader = new AsynImageLoader(); } return asynImageLoader; } /** * * @param imageView * 需要延迟加载图片的对象 * @param url * 图片的URL地址 * * @param roundedType * 圆角图片处理方式 * @param resId * 图片加载过程中显示的图片资源 */ public void showImageAsyn(ImageView imageView, String url, String roundedType, int resId, int itemWidth) { if(null == imageView) { return; } if(null == url || "".equals(url.trim()) || !QHClientApplication.getInstance().canLoadImg) { imageView.setImageResource(resId); return; } imageView.setTag(url); Bitmap bitmap = loadImageAsyn(url, roundedType, getImageCallback( imageView, resId), itemWidth); if (bitmap == null) { imageView.setImageResource(resId); } else { if (itemWidth > 0) { int width = bitmap.getWidth();// 获取真实宽高 int height = bitmap.getHeight(); LayoutParams lp = imageView.getLayoutParams(); lp.height = (height * itemWidth) / width;// 调整高度 imageView.setLayoutParams(lp); imageView.setImageBitmap(bitmap); } else { imageView.setImageBitmap(bitmap); } } } public Bitmap getBitmapAsyn(final String path, final int resId) { // 判断缓存中是否已经存在该图片 if (bitmapRefs.containsKey(path)) { // 取出软引用 BtimapRef rf = bitmapRefs.get(path); // 通过软引用,获取图片 Bitmap bitmap = rf.get(); // 如果该图片已经被释放,则将该path对应的键从Map中移除掉 if (bitmap == null) { bitmapRefs.remove(path); } else { // 如果图片未被释放,直接返回该图片 Log.i(TAG, "return image in cache" + path); return bitmap; } } else if(FileUtil.exists4ImageUrl(path)) { Bitmap bitmap = FileUtil.getImageBitmap(path); if(null != bitmap) { return bitmap; } } else { // 如果缓存中不常在该图片,则创建图片下载任务 Task task = new Task(); task.path = path; task.roundedType = "rect"; task.callback = new ImageCallback() { @Override public void loadImage(String _path, Bitmap bitmap, int itemWidth) { if (_path.equals(path)) { } else { addCacheBitmap(BitmapFactory .decodeResource(QHClientApplication .getInstance().getApplicationContext() .getResources(), resId), path); } } }; task.itemWidth = 0; Log.i(TAG, "new Task ," + path); if (!taskQueue.contains(task)) { taskQueue.add(task); // 唤醒任务下载队列 synchronized (runnable) { runnable.notify(); } } } // 缓存中没有图片则返回null return null; } /** * 获取原图 * * @param imageView * @param url * @param resId */ public void showImageAsyn(ImageView imageView, int position,String url, int resId) { showImageAsyn(imageView, url, "rect", resId, 0,position); } private void showImageAsyn(ImageView imageView, String url, String roundedType, int resId, int itemWidth, int position) { if(null == imageView) { return; } imageView.setScaleType(ScaleType.FIT_XY); if(null == url || "".equals(url.trim()) || !QHClientApplication.getInstance().canLoadImg) { imageView.setImageResource(resId); return; } imageView.setTag(url); Bitmap bitmap = loadImageAsyn(url, roundedType, getImageCallback( imageView, resId), itemWidth); if (bitmap == null) { imageView.setImageResource(resId); return; } else { if (itemWidth > 0) { int width = bitmap.getWidth();// 获取真实宽高 int height = bitmap.getHeight(); LayoutParams lp = imageView.getLayoutParams(); lp.height = (height * itemWidth) / width;// 调整高度 imageView.setLayoutParams(lp); imageView.setImageBitmap(bitmap); } else { imageView.setImageBitmap(bitmap); } } } public Bitmap loadImageAsyn(String path, String roundedType, ImageCallback callback, int itemWidth) { Bitmap bitmap = null; // 判断缓存中是否已经存在该图片 if (bitmapRefs.containsKey(path)) { // 取出软引用 BtimapRef rf = bitmapRefs.get(path); // 通过软引用,获取图片 bitmap = rf.get(); // 如果该图片已经被释放,则将该path对应的键从Map中移除掉 if (bitmap == null) { bitmapRefs.remove(path); } else { // 如果图片未被释放,直接返回该图片 Log.i(TAG, "return image in cache" + path); // return bitmap; } } else if(FileUtil.exists4ImageUrl(path)) { bitmap = FileUtil.getImageBitmap(path); if(null != bitmap) { // return bitmap; } } if(bitmap == null && StringUtils.isNotNull(path)) { // 如果缓存中不常在该图片,则创建图片下载任务 Task task = new Task(); task.path = path; task.roundedType = roundedType; task.callback = callback; task.itemWidth = itemWidth; Log.i(TAG, "new Task ," + path); if (!taskQueue.contains(task)) { taskQueue.add(task); // 唤醒任务下载队列 synchronized (runnable) { runnable.notify(); } } } // 缓存中没有图片则返回null return bitmap; } /** * * @param imageView * @param resId * 图片加载完成前显示的图片资源ID * @return */ private ImageCallback getImageCallback(final ImageView imageView, final int resId) { return new ImageCallback() { @Override public void loadImage(String path, Bitmap bitmap, int itemWidth) { if (path.equals(imageView.getTag().toString())) { if (itemWidth > 0) { if (bitmap != null) { int width = bitmap.getWidth();// 获取真实宽高 int height = bitmap.getHeight(); LayoutParams lp = imageView.getLayoutParams(); lp.height = (height * itemWidth) / width;// 调整高度 imageView.setLayoutParams(lp); imageView.setImageBitmap(bitmap); } else { imageView.setImageResource(resId); } } else { imageView.setImageBitmap(bitmap); } } else { imageView.setImageResource(resId); } } }; } private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // 子线程中返回的下载完成的任务 Task task = (Task) msg.obj; // 调用callback对象的loadImage方法,并将图片路径和图片回传给adapter task.callback.loadImage(task.path, task.bitmap, task.itemWidth); } }; private Runnable runnable = new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } while (isRunning) { // 当队列中还有未处理的任务时,执行下载任务 while (taskQueue.size() > 0) { // 获取第一个任务,并将之从任务队列中删除 Task task = taskQueue.remove(0); if (task != null) { // 将下载的图片添加到缓存 if(QHClientApplication.getInstance().canLoadImg) { task.bitmap = PicUtil.getbitmapAndwrite(task.path); // the new method to test to storage the image to SD card. // task.bitmap = PicUtil.getbitmap(task.path, task.roundedType); this is the old method, addCacheBitmap(task.bitmap, task.path); } if (handler != null) { // 创建消息对象,并将完成的任务添加到消息对象中 Message msg = handler.obtainMessage(); msg.obj = task; // 发送消息回主线程 handler.sendMessage(msg); } } } // 如果队列为空,则令线程等待 synchronized (this) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; // 回调接口 public interface ImageCallback { void loadImage(String path, Bitmap bitmap, int itemWidth); } class Task { // 下载任务的下载路径 String path; // 下载的图片 Bitmap bitmap; // 回调对象 ImageCallback callback; String roundedType; int itemWidth; @Override public boolean equals(Object o) { Task task = (Task) o; return task.path.equals(path); } } /** * 以软引用的方式对一个Bitmap对象的实例进行引用并保存该引用 */ private void addCacheBitmap(Bitmap bmp, String key) { cleanCache();// 清除垃圾引用 BtimapRef ref = new BtimapRef(bmp, q, key); bitmapRefs.put(key, ref); } private void cleanCache() { BtimapRef ref = null; while ((ref = (BtimapRef) q.poll()) != null) { bitmapRefs.remove(ref._key); } } // 清除Cache内的全部内容 public void clearCache() { cleanCache(); bitmapRefs.clear(); System.gc(); System.runFinalization(); } }