package com.keju.maomao; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.SoftReference; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Environment; import android.os.Handler; import android.util.Log; import com.keju.maomao.util.StringUtil; /** * 异步加载图片 * @author Zhoujun */ public class AsyncImageLoader { public static final String IMAGE_TYPE_OF_PNG = "png"; public static final String IMAGE_TYPE_OF_JPEG = "jpg"; // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动) public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>(); private static ExecutorService executorService; // 固定五个线程来执行任务 private final Handler handler = new Handler(); private static AsyncImageLoader instance; public static AsyncImageLoader getInstance() { if (instance == null) { instance = new AsyncImageLoader(); executorService = Executors.newFixedThreadPool(5); } return instance; } public void clear(){ for (Entry<String, SoftReference<Drawable>> entry : imageCache.entrySet()) { Drawable drawable = entry.getValue().get(); if(drawable!=null) drawable.setCallback(null); } imageCache.clear(); System.gc(); } private List<String> imageUrlLoadTask = new ArrayList<String>(); public boolean containsUrl(String url){ return imageUrlLoadTask.contains(url); } public Drawable loadAsynLocalDrawable(final String imageUrl,final ImageCallback callback) { if(containsUrl(imageUrl)) return null; imageUrlLoadTask.add(imageUrl); executorService.submit(new Runnable() { public void run() { try { final Drawable drawable = fetchDrawable(imageUrl); handler.post(new Runnable() { public void run() { callback.imageLoaded(drawable,imageUrl); imageUrlLoadTask.remove(imageUrl); } }); } catch (Exception e) { throw new RuntimeException(e); } } }); return null; } /** * 异步加载图片 * @param imageUrl * @param callback * @return */ public Drawable loadAsynSoftRefeDrawable(final Map<String, SoftReference<Drawable>> imageCache,int maxSize,final String imageUrl,final ImageCallback callback) { // 如果缓存过就从缓存中取出数据 if (imageCache.containsKey(imageUrl)) { SoftReference<Drawable> softReference = imageCache.get(imageUrl); if (softReference.get() != null) { return softReference.get(); } } if(containsUrl(imageUrl)) return null; imageUrlLoadTask.add(imageUrl); if(imageCache.size() > maxSize){//每超过15张照片清空 clear(); } executorService.submit(new Runnable() { public void run() { try { final Drawable drawable = fetchDrawable(imageUrl); if(drawable != null){ imageCache.put(imageUrl, new SoftReference<Drawable>(drawable)); } handler.post(new Runnable() { public void run() { callback.imageLoaded(drawable,imageUrl); imageUrlLoadTask.remove(imageUrl); } }); } catch (Exception e) { throw new RuntimeException(e); } } }); return null; } public Drawable loadAsynDrawable(final Map<String, Drawable> imageCache,int maxSize,final String imageUrl,final ImageCallback callback) { // 如果缓存过就从缓存中取出数据 if (imageCache.containsKey(imageUrl)) { return imageCache.get(imageUrl); } if(containsUrl(imageUrl)) return null; imageUrlLoadTask.add(imageUrl); if(imageCache.size() > maxSize){//每超过15张照片清空 clear(); } executorService.submit(new Runnable() { public void run() { try { final Drawable drawable = fetchDrawable(imageUrl); if(drawable != null){ imageCache.put(imageUrl, drawable); } handler.post(new Runnable() { public void run() { callback.imageLoaded(drawable,imageUrl); imageUrlLoadTask.remove(imageUrl); } }); } catch (Exception e) { throw new RuntimeException(e); } } }); return null; } /** * * @param imageUrl * 图像url地址 * @param callback * 回调接口 * @return 返回内存中缓存的图像,第一次加载返回null */ public Drawable loadDrawable(final String imageUrl, final ImageCallback callback) { // 如果缓存过就从缓存中取出数据 if (imageCache.containsKey(imageUrl)) { SoftReference<Drawable> softReference = imageCache.get(imageUrl); if (softReference.get() != null) { return softReference.get(); } } if(containsUrl(imageUrl)) return null; imageUrlLoadTask.add(imageUrl); // 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中 executorService.submit(new Runnable() { public void run() { try { final Drawable drawable = fetchDrawable(imageUrl); if(drawable != null){ imageCache.put(imageUrl, new SoftReference<Drawable>(drawable)); } handler.post(new Runnable() { public void run() { callback.imageLoaded(drawable,imageUrl); imageUrlLoadTask.remove(imageUrl); } }); } catch (Exception e) { throw new RuntimeException(e); } } }); return null; } /** * * @param imageUrl * 图像url地址 * @param callback * 回调接口 * @return 返回内存中缓存的图像,第一次加载返回null */ public Drawable loadDrawableAppointType(final String imageUrl, final ImageCallback callback, final String imgType) { // 如果缓存过就从缓存中取出数据 if (imageCache.containsKey(imageUrl)) { SoftReference<Drawable> softReference = imageCache.get(imageUrl); if (softReference.get() != null) { return softReference.get(); } } if(containsUrl(imageUrl)) return null; imageUrlLoadTask.add(imageUrl); // 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中 executorService.submit(new Runnable() { public void run() { try { final Drawable drawable = fetchDrawableAppointType(imageUrl, imgType); if(drawable != null){ imageCache.put(imageUrl, new SoftReference<Drawable>(drawable)); } handler.post(new Runnable() { public void run() { callback.imageLoaded(drawable,imageUrl); imageUrlLoadTask.remove(imageUrl); } }); } catch (Exception e) { throw new RuntimeException(e); } } }); return null; } //存放去远程图片的任务 private static Map<String,String> fetchTaskMap = new HashMap<String, String>(); public static final File PHOTO_DIR = new File(Environment.getExternalStorageDirectory()+ "/"+Constants.APP_DIR_NAME+"/"); File file = new File(PHOTO_DIR, ".nomedia"); public Drawable fetchDrawable(String imageUrl) { InputStream is = null; File localFile = null; try { Drawable drawable; if(!PHOTO_DIR.exists()) PHOTO_DIR.mkdirs(); localFile = new File(PHOTO_DIR,StringUtil.createImageName(imageUrl)); if(!localFile.exists() || localFile.length() <= 0){ if(!fetchTaskMap.containsKey(imageUrl)){ fetchTaskMap.put(imageUrl, imageUrl); is = fetch(imageUrl); if(is==null) throw new RuntimeException("stream is null"); // Bitmap bm = null; // byte[] data = readStream(is); // if(data!=null){ // bm = BitmapFactory.decodeByteArray(data, 0, data.length); // } Bitmap bm = BitmapFactory.decodeStream(is); if(bm!=null){ OutputStream outStream = new FileOutputStream(localFile,false); bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream); outStream.flush(); outStream.close(); } is.close(); if(bm!=null && !bm.isRecycled()) bm.recycle(); fetchTaskMap.remove(imageUrl); } } // localFile = new File(recordBean.getPhotoUrl()); if(localFile.length() <= 0){ return null; } drawable = fetchLocal(localFile.getPath()); return drawable; } catch (Exception e) { //出现异常则删除原始文件 if(localFile!=null && localFile.exists()){ localFile.delete(); } Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e); } finally{//关闭流 try { //删除fetch任务 if(fetchTaskMap.containsKey(imageUrl)){ fetchTaskMap.remove(imageUrl); } if(is!=null){ is.close(); } } catch (IOException e) { e.printStackTrace(); } } return null; } public Drawable fetchDrawableAppointType(String imageUrl, String imgType) { InputStream is = null; File localFile = null; try { Drawable drawable; if(!PHOTO_DIR.exists()) PHOTO_DIR.mkdirs(); if(!file.exists()) file.createNewFile(); localFile = new File(PHOTO_DIR,StringUtil.createImageName(imageUrl)); if(!localFile.exists() || localFile.length() <= 0){ if(!fetchTaskMap.containsKey(imageUrl)){ fetchTaskMap.put(imageUrl, imageUrl); is = fetch(imageUrl); if(is==null) throw new RuntimeException("stream is null"); Bitmap bm = null; byte[] data = readStream(is); if(data!=null){ bm = BitmapFactory.decodeByteArray(data, 0, data.length); } // bm = BitmapFactory.decodeStream(is); if(bm!=null){ OutputStream outStream = new FileOutputStream(localFile,false); if(IMAGE_TYPE_OF_PNG.equals(imgType)){ bm.compress(Bitmap.CompressFormat.PNG, 100, outStream); } if(IMAGE_TYPE_OF_JPEG.equals(imgType)){ bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream); } outStream.flush(); outStream.close(); } is.close(); if(bm!=null && !bm.isRecycled()) bm.recycle(); fetchTaskMap.remove(imageUrl); } } // localFile = new File(recordBean.getPhotoUrl()); if(localFile.length() <= 0){ return null; } drawable = fetchLocal(localFile.getPath()); return drawable; } catch (Exception e) { //出现异常则删除原始文件 if(localFile!=null && localFile.exists()){ localFile.delete(); } Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e); } finally{//关闭流 try { //删除fetch任务 if(fetchTaskMap.containsKey(imageUrl)){ fetchTaskMap.remove(imageUrl); } if(is!=null){ is.close(); } } catch (IOException e) { e.printStackTrace(); } } return null; } /** * 得到图片字节流 数组大小 * @param inStream * @return * @throws Exception */ public static byte[] readStream(InputStream inStream) throws Exception{ ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while( (len=inStream.read(buffer,0,1024)) != -1){ outStream.write(buffer, 0, len); } outStream.flush(); outStream.close(); inStream.close(); return outStream.toByteArray(); } /** * 从网络获取图片 * @param urlString * @return * @throws MalformedURLException * @throws IOException */ public InputStream fetch(String urlString) throws MalformedURLException, IOException { DefaultHttpClient httpClient = new DefaultHttpClient(); HttpGet request = new HttpGet(urlString); HttpResponse response = httpClient.execute(request); return response.getEntity().getContent(); } /** * 从本地获取图片 * @param urlString * @return */ public Drawable fetchLocal(String urlString){ try{ BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeFile(urlString, opts); opts.inSampleSize = computeSampleSize(opts, -1, 512*512); //这里一定要将其设置回false,因为之前我们将其设置成了true opts.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeFile(urlString,opts); return new BitmapDrawable(bitmap); }catch(Exception e){ Log.e(this.getClass().getSimpleName(), "fetchLocal failed", e); return null; } } // 对外界开放的回调接口 public interface ImageCallback { // 注意 此方法是用来设置目标对象的图像资源 public void imageLoaded(Drawable imageDrawable,String imageUrl); } /** * 计算缩放比例 * @param options * @param minSideLength * @param maxNumOfPixels * @return */ public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels); int roundedSize; if (initialSize <= 8) { roundedSize = 1; while (roundedSize < initialSize) { roundedSize <<= 1; } } else { roundedSize = (initialSize + 7) / 8 * 8; } return roundedSize; } private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { double w = options.outWidth; double h = options.outHeight; int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math .sqrt(w * h / maxNumOfPixels)); int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math .floor(w / minSideLength), Math.floor(h / minSideLength)); if (upperBound < lowerBound) { return lowerBound; } if ((maxNumOfPixels == -1) && (minSideLength == -1)) { return 1; } else if (minSideLength == -1) { return lowerBound; } else { return upperBound; } } }