package com.android.pc.ioc.image; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.HashMap; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Build; import android.os.Handler; import android.os.Message; import android.widget.ImageView; import com.android.pc.ioc.app.Ioc; import com.android.pc.ioc.image.ImageLoadManager.Coding; import com.android.pc.util.Handler_System; /** * 图片下载工具类 */ public class ImageWorker { private static final int FADE_IN_TIME = 200; private static final int start = 0; private static final int process = 1; private static final int fail = 2; private static final int finish = 3; private ImageCache mImageCache; private Bitmap mLoadingBitmap; private boolean mFadeInBitmap = true; private boolean mExitTasksEarly = false; protected boolean mPauseWork = false; private final Object mPauseWorkLock = new Object(); protected Resources mResources; protected DisplayerLister lister; private boolean isDecode = false; protected ImageWorker(Context context) { mResources = context.getResources(); } protected int mImageWidth; protected int mImageHeight; /** * 初始化的时候 需要设置大小 * * @param context * @param imageWidth * @param imageHeight */ public ImageWorker(Context context, int imageWidth, int imageHeight) { setImageSize(imageWidth, imageHeight); mResources = context.getResources(); } /** * 初始化的时候 需要设置大小 * * @param context * @param imageSize */ public ImageWorker(Context context, int imageSize) { setImageSize(imageSize); mResources = context.getResources(); } public void useDecode(boolean isDecode) { this.isDecode = isDecode; } public boolean isDecode() { return isDecode; } /** * 加载网络图片到ImageView上(当然也可以重写{@link ImageWorker#processBitmap(Object)}来实现图片获取过程)<br> * 如果图片在内存和本地缓存ImageWorker中存在,则直接 <br> * 否则将开启{@link AsyncTask} 下载图片 * * @param data * 图片下载链接 * @param imageView * 图片显示组件 */ public void loadImage(Object data, ImageView imageView) { if (data == null) { return; } if (data.toString().startsWith("http")) { if (data.toString().indexOf("?") != -1) { data = data + "&w=" + getW() + "&h=" + getH(); } else { data = data + "?w=" + getW() + "&h=" + getH(); } } BitmapDrawable value = null; mImageCache = ImageLoadManager.instance().getmImageCache(); if (mImageCache != null) { value = mImageCache.getBitmapFromMemCache(String.valueOf(data)); if (value!=null) { if (value.getBitmap().getConfig() == null) { Ioc.getIoc().getLogger().d("图片解析错误-----重新下载"); value = null; } } } // 如果图片为空 if (value != null) { imageView.setImageDrawable(value); // 如果图片不为空 则直接设置 if (value.getBitmap() != null) { finish(value.getBitmap(), imageView, lister); } } else if (cancelPotentialWork(data, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView, lister); final AsyncDrawable asyncDrawable = new AsyncDrawable(mResources, mLoadingBitmap, task); imageView.setImageDrawable(asyncDrawable); // 通过线程池执行下载进程 task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR); } } public void loadImage(Object data, ImageView imageView, DisplayerLister lister) { this.lister = lister; loadImage(data, imageView); } /** * 设置loading的图片 * * @param bitmap */ public void setLoadingImage(Bitmap bitmap) { mLoadingBitmap = bitmap; } /** * 设置loading的图片的ID * * @param resId */ public void setLoadingImage(int resId) { mLoadingBitmap = BitmapFactory.decodeResource(mResources, resId); } /** * 淡入淡出 */ public void setImageFadeIn(boolean fadeIn) { mFadeInBitmap = fadeIn; } public void setExitTasksEarly(boolean exitTasksEarly) { mExitTasksEarly = exitTasksEarly; setPauseWork(false); } private class Entity { public int process; public ImageView imageView; public Bitmap bitmap; public DisplayerLister lister; } protected void process(int process, ImageView imageView, DisplayerLister lister) { if (lister == null) { return; } Message msg = handler.obtainMessage(); Entity entity = new Entity(); entity.process = process; entity.imageView = imageView; entity.lister = lister; msg.obj = entity; msg.what = process; msg.sendToTarget(); }; protected void finish(Bitmap bitmap, ImageView imageView, DisplayerLister lister) { if (lister == null) { return; } Message msg = handler.obtainMessage(); Entity entity = new Entity(); entity.bitmap = bitmap; entity.imageView = imageView; entity.lister = lister; msg.obj = entity; msg.what = finish; msg.sendToTarget(); }; protected void fail(ImageView imageView, DisplayerLister lister) { if (lister == null) { return; } Message msg = handler.obtainMessage(); Entity entity = new Entity(); entity.imageView = imageView; entity.lister = lister; msg.obj = entity; msg.what = fail; msg.sendToTarget(); }; protected void start(ImageView imageView, DisplayerLister lister) { if (lister == null) { return; } Message msg = handler.obtainMessage(); Entity entity = new Entity(); entity.imageView = imageView; entity.lister = lister; msg.obj = entity; msg.what = start; msg.sendToTarget(); }; Handler handler = new Handler() { public void handleMessage(Message msg) { Entity entity = (Entity) msg.obj; switch (msg.what) { case start: entity.lister.startLoader(entity.imageView); break; case process: entity.lister.progressLoader(entity.process, entity.imageView); break; case fail: entity.lister.failLoader(entity.imageView); break; case finish: entity.lister.finishLoader(entity.bitmap, entity.imageView); break; } }; }; /** * @return 返回当前对象使用的缓存 */ protected ImageCache getImageCache() { return mImageCache; } /** * 取消这个imageview所对应的任务 * * @param imageView */ public static void cancelWork(ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { bitmapWorkerTask.cancel(true); final Object bitmapData = bitmapWorkerTask.mData; Ioc.getIoc().getLogger().d("cancelWork - cancelled work for " + bitmapData); } } /** * <h1>第一步</h1> 根据imageView获取到AsyncDrawable<br> * <h1>第二步</h1>从AsyncDrawable获得BitmapWorkerTask<br> * <h1>第三步</h1>根据BitmapWorkerTask中的url判断当前下载的url和之前是否一样<br> * 如果一样则返回false无需再下载<br> * <h1>第四步</h1>如果不一样则说明 当前imageview需要显示的图片和之前图片不一样,取消之前下载进程 返回true * */ public static boolean cancelPotentialWork(Object data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final Object bitmapData = bitmapWorkerTask.mData; // 判断当前的Url和之前的url是否一样 if (bitmapData == null || !bitmapData.equals(data)) { bitmapWorkerTask.cancel(true); Ioc.getIoc().getLogger().d("cancelPotentialWork - cancelled work for " + data); } else { return false; } } return true; } /** * @param imageView * 图片控件 * @return 返回和当前imageview有关的任务 如果不存在则返回null */ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncDrawable) { final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null; } /** * 异步下载图片 继承的一个自定义的异步下载进程 */ private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> { /** 图片的来源 **/ private Object mData; // 弱引用 存储了视图 private final WeakReference<ImageView> imageViewReference; protected final DisplayerLister lister; public BitmapWorkerTask(Object data, ImageView imageView, DisplayerLister lister) { mData = data; imageViewReference = new WeakReference<ImageView>(imageView); this.lister = lister; } /** * 后台进程 */ @Override protected BitmapDrawable doInBackground(Void... params) { Ioc.getIoc().getLogger().d("doInBackground - starting work"); final String dataString = String.valueOf(mData); Bitmap bitmap = null; BitmapDrawable drawable = null; // 暂停下载的时候这里被锁住了 等待唤醒 // 这里也可能被取消了 synchronized (mPauseWorkLock) { while (mPauseWork && !isCancelled()) { try { mPauseWorkLock.wait(); } catch (InterruptedException e) { } } } // 如果缓存不为空 没有被取消 当前显示的imageview不为空 则去从本地缓存中获取 if (mImageCache != null && !isCancelled() && getAttachedImageView() != null && !mExitTasksEarly) { bitmap = mImageCache.getBitmapFromDiskCache(dataString, getW(), getH(), ImageWorker.this); if (bitmap!=null) { if (bitmap.getConfig() == null) { Ioc.getIoc().getLogger().d("图片解析错误-----重新下载"); bitmap = null; } } } // 下载图片 通过自定义下载模块 if (bitmap == null && !isCancelled() && getAttachedImageView() != null && !mExitTasksEarly) { bitmap = processBitmap(mData, imageViewReference.get()); } // 如果图片不为空 if (bitmap != null) { // if (Utils.hasHoneycomb()) { // // 如果版本大于3.0则把bitmap包裹到BitmapDrawable中 // drawable = new BitmapDrawable(mResources, bitmap); // } else { // 小于3.0的情况下 自定义一个BitmapDrawable用来自动回收图片 drawable = new RecyclingBitmapDrawable(mResources, bitmap); // } if (mImageCache != null) { mImageCache.addBitmapToCache(dataString, drawable); } } Ioc.getIoc().getLogger().d("doInBackground - finished work"); return drawable; } /** * 图片下载完成以后,调用imageview进行绘制 */ @Override protected void onPostExecute(BitmapDrawable value) { // 如果取消 或者 被标志位退出 则返回Null if (isCancelled() || mExitTasksEarly) { value = null; } final ImageView imageView = getAttachedImageView(); if (value != null && imageView != null) { Ioc.getIoc().getLogger().d("onPostExecute - setting bitmap"); setImageDrawable(imageView, value); if (value.getBitmap() != null) { finish(value.getBitmap(), imageView, lister); } } if (value == null) { fail(imageView, lister); } } @Override protected void onCancelled(BitmapDrawable value) { super.onCancelled(value); synchronized (mPauseWorkLock) { mPauseWorkLock.notifyAll(); } } /** * 返回该任务指向的imageview 否则返回null */ private ImageView getAttachedImageView() { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask) { return imageView; } return null; } @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); } } /** * 用来显示Bitmap的载体, 包含了下载线程的引用 */ private static class AsyncDrawable extends BitmapDrawable { private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask); } public BitmapWorkerTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } } /** * Called when the processing is complete and the final drawable should be set on the ImageView. * * @param imageView * @param drawable */ private void setImageDrawable(ImageView imageView, Drawable drawable) { if (mFadeInBitmap) { final TransitionDrawable td = new TransitionDrawable(new Drawable[] { new ColorDrawable(android.R.color.transparent), drawable }); // imageView.setBackgroundDrawable(new BitmapDrawable(mResources, mLoadingBitmap)); imageView.setImageDrawable(td); td.startTransition(FADE_IN_TIME); } else { imageView.setImageDrawable(drawable); } } /** * 可以用来暂停图片的下载 一半用在列表 {@link android.widget.AbsListView.OnScrollListener}中来让滑动更为平滑 * <p> * 为了让下载更加安全 请在你得fragment或者activity {@link android.app.Activity#onPause()}终止之前 请设置setPauseWork(false) */ public void setPauseWork(boolean pauseWork) { synchronized (mPauseWorkLock) { mPauseWork = pauseWork; if (!mPauseWork) { mPauseWorkLock.notifyAll(); } } } /** * Set the target image width and height. * * @param width * @param height */ public void setImageSize(int width, int height) { HashMap<String, Integer> data = Handler_System.getDisplayMetrics(); if (width == 0) { width = data.get(Handler_System.systemWidth); } if (height == 0) { height = data.get(Handler_System.systemHeight); } mImageWidth = width; mImageHeight = height; } /** * 设置图片的高宽 高宽一样 * * @param size */ public void setImageSize(int size) { setImageSize(size, size); } /** * 图片解析的主方法<br> * 参数为资源ID * * @param resId * @return */ private Bitmap processBitmap(int resId) { Ioc.getIoc().getLogger().d("图片下载开始 - " + resId); return decodeSampledBitmapFromResource(mResources, resId, mImageWidth, mImageHeight, getImageCache()); } protected Bitmap processBitmap(Object data, ImageView imageView) { return processBitmap(Integer.parseInt(String.valueOf(data))); } /** * 从资源文件加载图片 * * @author gdpancheng@gmail.com 2014-5-19 下午2:14:13 * @param res * @param resId * @param reqWidth * @param reqHeight * @param cache * @return Bitmap */ public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight, ImageCache cache) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); if (Utils.hasHoneycomb()) { addInBitmapOptions(options, cache); } options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } /** * 从本地文件加载图片 * * @author gdpancheng@gmail.com 2014-5-19 下午2:15:23 * @param filename * @param reqWidth * @param reqHeight * @param cache * @return Bitmap */ public Bitmap decodeSampledBitmapFromFile(String url, String filename, int reqWidth, int reqHeight, ImageCache cache) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; Coding coding = ImageLoadManager.instance().getCoding(); byte[] buffer = null; if (coding == null || isDecode == false) { BitmapFactory.decodeFile(filename, options); } else if (isDecode && coding != null) { InputStream in = null; try { in = new FileInputStream(filename); if (url.toLowerCase().indexOf(".jpg") != -1||url.toLowerCase().indexOf(".jpeg") != -1) { buffer = coding.decodeJPG(in.available(), in); } else { buffer = coding.decodePNG(in.available(), in); } in.close(); BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options); } catch (Exception e) { e.printStackTrace(); } } options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); if (Utils.hasHoneycomb()) { addInBitmapOptions(options, cache); } options.inJustDecodeBounds = false; if (coding == null || isDecode == false) { return BitmapFactory.decodeFile(filename, options); } else if (isDecode && coding != null) { try { return BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options); } catch (Exception e) { e.printStackTrace(); } finally{ buffer = null; } } return null; } public Bitmap decodeSampledBitmapFromDescriptor(FileDescriptor fileDescriptor, int reqWidth, int reqHeight, ImageCache cache) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; if (Utils.hasHoneycomb()) { addInBitmapOptions(options, cache); } return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) { options.inMutable = true; if (cache != null) { Bitmap inBitmap = cache.getBitmapFromReusableSet(options); if (inBitmap != null) { options.inBitmap = inBitmap; } } } /** * 根据需要的高宽,对图片进行缩放 TODO(这里用一句话描述这个方法的作用) * * @author gdpancheng@gmail.com 2014-5-19 下午2:19:56 * @param options * @param reqWidth * @param reqHeight * @return int */ public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } long totalPixels = width * height / inSampleSize; final long totalReqPixelsCap = reqWidth * reqHeight * 2; while (totalPixels > totalReqPixelsCap) { inSampleSize *= 2; totalPixels /= 2; } } return inSampleSize; } protected int getW() { return mImageWidth; } protected int getH() { return mImageHeight; } }