/** * Copyright (c) 2012-2013, Michael Yang 杨福海 (www.yangfuhai.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.tsz.afinal; 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.Drawable; import android.text.TextUtils; import android.util.DisplayMetrics; import android.view.View; import android.widget.ImageView; import net.tsz.afinal.bitmap.core.BitmapCache; import net.tsz.afinal.bitmap.core.BitmapDecorator; import net.tsz.afinal.bitmap.core.BitmapDisplayConfig; import net.tsz.afinal.bitmap.core.BitmapProcess; import net.tsz.afinal.bitmap.display.Displayer; import net.tsz.afinal.bitmap.display.SimpleDisplayer; import net.tsz.afinal.bitmap.download.Downloader; import net.tsz.afinal.bitmap.download.SimpleDownloader; import net.tsz.afinal.core.AsyncTask; import net.tsz.afinal.utils.Utils; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; public class FinalBitmap { private BitmapDecorator mDecorator; private FinalBitmapConfig mConfig; private BitmapCache mImageCache; private BitmapProcess mBitmapProcess; private boolean mExitTasksEarly = false; private boolean mPauseWork = false; private final Object mPauseWorkLock = new Object(); private Context mContext; private boolean mInit = false ; private ExecutorService bitmapLoadAndDisplayExecutor; private static FinalBitmap mFinalBitmap; ////////////////////////// config method start//////////////////////////////////// private FinalBitmap(Context context) { mContext = context; mConfig = new FinalBitmapConfig(context); configDiskCachePath(Utils.getDiskCacheDir(context, "afinalCache").getAbsolutePath());//配置缓存路径 configDisplayer(new SimpleDisplayer());//配置显示器 configDownlader(new SimpleDownloader());//配置下载器 } /** * 创建finalbitmap * @param ctx * @return */ public static synchronized FinalBitmap create(Context ctx){ if(mFinalBitmap == null){ mFinalBitmap = new FinalBitmap(ctx.getApplicationContext()); } return mFinalBitmap; } public FinalBitmap configBitmapDecorator(BitmapDecorator decorator) { mDecorator = decorator; return this; } /** * 设置图片正在加载的时候显示的图片 * @param bitmap */ public FinalBitmap configLoadingImage(Bitmap bitmap) { mConfig.defaultDisplayConfig.setLoadingBitmap(bitmap); return this; } /** * 设置图片正在加载的时候显示的图片 * @param bitmap */ public FinalBitmap configLoadingImage(int resId) { mConfig.defaultDisplayConfig.setLoadingBitmap(BitmapFactory.decodeResource(mContext.getResources(), resId)); return this; } /** * 设置图片加载失败时候显示的图片 * @param bitmap */ public FinalBitmap configLoadfailImage(Bitmap bitmap) { mConfig.defaultDisplayConfig.setLoadfailBitmap(bitmap); return this; } /** * 设置图片加载失败时候显示的图片 * @param resId */ public FinalBitmap configLoadfailImage(int resId) { mConfig.defaultDisplayConfig.setLoadfailBitmap(BitmapFactory.decodeResource(mContext.getResources(), resId)); return this; } /** * 配置默认图片的小的高度 * @param bitmapHeight */ public FinalBitmap configBitmapMaxHeight(int bitmapHeight){ mConfig.defaultDisplayConfig.setBitmapHeight(bitmapHeight); return this; } /** * 配置默认图片的小的宽度 * @param bitmapHeight */ public FinalBitmap configBitmapMaxWidth(int bitmapWidth){ mConfig.defaultDisplayConfig.setBitmapWidth(bitmapWidth); return this; } /** * 设置下载器,比如通过ftp或者其他协议去网络读取图片的时候可以设置这项 * @param downlader * @return */ public FinalBitmap configDownlader(Downloader downlader){ mConfig.downloader = downlader; return this; } /** * 设置显示器,比如在显示的过程中显示动画等 * @param displayer * @return */ public FinalBitmap configDisplayer(Displayer displayer){ mConfig.displayer = displayer; return this; } /** * 配置磁盘缓存路径 * @param strPath * @return */ public FinalBitmap configDiskCachePath(String strPath){ if(!TextUtils.isEmpty(strPath)){ mConfig.cachePath = strPath; } return this; } /** * 配置内存缓存大小 大于2MB以上有效 * @param size 缓存大小 */ public FinalBitmap configMemoryCacheSize(int size){ mConfig.memCacheSize = size; return this; } /** * 设置应缓存的在APK总内存的百分比,优先级大于configMemoryCacheSize * @param percent 百分比,值的范围是在 0.05 到 0.8之间 */ public FinalBitmap configMemoryCachePercent(float percent){ mConfig.memCacheSizePercent = percent; return this; } /** * 设置磁盘缓存大小 5MB 以上有效 * @param size */ public FinalBitmap configDiskCacheSize(int size){ mConfig.diskCacheSize = size; return this; } /** * 设置加载图片的线程并发数量 * @param size */ public FinalBitmap configBitmapLoadThreadSize(int size){ if(size >= 1) mConfig.poolSize = size; return this; } /** * 配置是否立即回收图片资源 * @param recycleImmediately * @return */ public FinalBitmap configRecycleImmediately(boolean recycleImmediately){ mConfig.recycleImmediately = recycleImmediately; return this; } /** * 初始化finalBitmap * @return */ private FinalBitmap init(){ if( ! mInit ){ BitmapCache.ImageCacheParams imageCacheParams = new BitmapCache.ImageCacheParams(mConfig.cachePath); if(mConfig.memCacheSizePercent>0.05 && mConfig.memCacheSizePercent<0.8){ imageCacheParams.setMemCacheSizePercent(mContext, mConfig.memCacheSizePercent); }else{ if(mConfig.memCacheSize > 1024 * 1024 * 2){ imageCacheParams.setMemCacheSize(mConfig.memCacheSize); }else{ //设置默认的内存缓存大小 imageCacheParams.setMemCacheSizePercent(mContext, 0.3f); } } if(mConfig.diskCacheSize > 1024 * 1024 * 5) imageCacheParams.setDiskCacheSize(mConfig.diskCacheSize); imageCacheParams.setRecycleImmediately(mConfig.recycleImmediately); //init Cache mImageCache = new BitmapCache(imageCacheParams); //init Executors bitmapLoadAndDisplayExecutor = Executors.newFixedThreadPool(mConfig.poolSize,new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); // 设置线程的优先级别,让线程先后顺序执行(级别越高,抢到cpu执行的时间越多) t.setPriority(Thread.NORM_PRIORITY - 1); return t; } }); //init BitmapProcess mBitmapProcess = new BitmapProcess(mConfig.downloader, mImageCache, mDecorator); mInit = true ; } return this; } ////////////////////////// config method end//////////////////////////////////// public void display(View imageView,String uri){ doDisplay(imageView,uri,null); } public void display(View imageView,String uri,int imageWidth,int imageHeight){ BitmapDisplayConfig displayConfig = configMap.get(imageWidth+"_"+imageHeight); if(displayConfig==null){ displayConfig = getDisplayConfig(); displayConfig.setBitmapHeight(imageHeight); displayConfig.setBitmapWidth(imageWidth); configMap.put(imageWidth+"_"+imageHeight, displayConfig); } doDisplay(imageView,uri,displayConfig); } public void display(View imageView,String uri,Bitmap loadingBitmap){ BitmapDisplayConfig displayConfig = configMap.get(String.valueOf(loadingBitmap)); if(displayConfig==null){ displayConfig = getDisplayConfig(); displayConfig.setLoadingBitmap(loadingBitmap); configMap.put(String.valueOf(loadingBitmap), displayConfig); } doDisplay(imageView,uri,displayConfig); } public void display(View imageView,String uri,Bitmap loadingBitmap,Bitmap laodfailBitmap){ BitmapDisplayConfig displayConfig = configMap.get(String.valueOf(loadingBitmap)+"_"+String.valueOf(laodfailBitmap)); if(displayConfig==null){ displayConfig = getDisplayConfig(); displayConfig.setLoadingBitmap(loadingBitmap); displayConfig.setLoadfailBitmap(laodfailBitmap); configMap.put(String.valueOf(loadingBitmap)+"_"+String.valueOf(laodfailBitmap), displayConfig); } doDisplay(imageView,uri,displayConfig); } public void display(View imageView,String uri,int imageWidth,int imageHeight,Bitmap loadingBitmap,Bitmap laodfailBitmap){ BitmapDisplayConfig displayConfig = configMap.get(imageWidth+"_"+imageHeight+"_"+String.valueOf(loadingBitmap)+"_"+String.valueOf(laodfailBitmap)); if(displayConfig==null){ displayConfig = getDisplayConfig(); displayConfig.setBitmapHeight(imageHeight); displayConfig.setBitmapWidth(imageWidth); displayConfig.setLoadingBitmap(loadingBitmap); displayConfig.setLoadfailBitmap(laodfailBitmap); configMap.put(imageWidth+"_"+imageHeight+"_"+String.valueOf(loadingBitmap)+"_"+String.valueOf(laodfailBitmap), displayConfig); } doDisplay(imageView,uri,displayConfig); } public void display(View imageView,String uri,BitmapDisplayConfig config){ doDisplay(imageView,uri,config); } private void doDisplay(View imageView, String uri, BitmapDisplayConfig displayConfig) { if(!mInit ){ init(); } if (TextUtils.isEmpty(uri) || imageView == null) { return; } if(displayConfig == null) displayConfig = mConfig.defaultDisplayConfig; Bitmap bitmap = null; if (mImageCache != null) { bitmap = mImageCache.getBitmapFromMemoryCache(uri); } if (bitmap != null) { if(imageView instanceof ImageView){ ((ImageView)imageView).setImageBitmap(bitmap); }else{ imageView.setBackgroundDrawable(new BitmapDrawable(bitmap)); } }else if (checkImageTask(uri, imageView)) { final BitmapLoadAndDisplayTask task = new BitmapLoadAndDisplayTask(imageView, displayConfig ); //设置默认图片 final AsyncDrawable asyncDrawable = new AsyncDrawable(mContext.getResources(), displayConfig.getLoadingBitmap(), task); if(imageView instanceof ImageView){ ((ImageView)imageView).setImageDrawable(asyncDrawable); }else{ imageView.setBackgroundDrawable(asyncDrawable); } task.executeOnExecutor(bitmapLoadAndDisplayExecutor, uri); } } private HashMap<String, BitmapDisplayConfig> configMap = new HashMap<String, BitmapDisplayConfig>(); private BitmapDisplayConfig getDisplayConfig(){ BitmapDisplayConfig config = new BitmapDisplayConfig(); config.setAnimation(mConfig.defaultDisplayConfig.getAnimation()); config.setAnimationType(mConfig.defaultDisplayConfig.getAnimationType()); config.setBitmapHeight(mConfig.defaultDisplayConfig.getBitmapHeight()); config.setBitmapWidth(mConfig.defaultDisplayConfig.getBitmapWidth()); config.setLoadfailBitmap(mConfig.defaultDisplayConfig.getLoadfailBitmap()); config.setLoadingBitmap(mConfig.defaultDisplayConfig.getLoadingBitmap()); return config; } private void clearCacheInternalInBackgroud() { if (mImageCache != null) { mImageCache.clearCache(); } } private void clearDiskCacheInBackgroud(){ if (mImageCache != null) { mImageCache.clearDiskCache(); } } private void clearCacheInBackgroud(String key){ if (mImageCache != null) { mImageCache.clearCache(key); } } private void clearDiskCacheInBackgroud(String key){ if (mImageCache != null) { mImageCache.clearDiskCache(key); } } /** * 执行过此方法后,FinalBitmap的缓存已经失效,建议通过FinalBitmap.create()获取新的实例 * @author fantouch */ private void closeCacheInternalInBackgroud() { if (mImageCache != null) { mImageCache.close(); mImageCache = null; mFinalBitmap = null; } } /** * 网络加载bitmap * @param data * @return */ private Bitmap processBitmap(String uri,BitmapDisplayConfig config) { if (mBitmapProcess != null) { return mBitmapProcess.getBitmap(uri,config); } return null; } /** * 从缓存(内存缓存和磁盘缓存)中直接获取bitmap,注意这里有io操作,最好不要放在ui线程执行 * @param key * @return */ public Bitmap getBitmapFromCache(String key){ Bitmap bitmap = getBitmapFromMemoryCache(key); if(bitmap == null) bitmap = getBitmapFromDiskCache(key); return bitmap; } /** * 从内存缓存中获取bitmap * @param key * @return */ public Bitmap getBitmapFromMemoryCache(String key){ return mImageCache.getBitmapFromMemoryCache(key); } /** * 从磁盘缓存中获取bitmap,,注意这里有io操作,最好不要放在ui线程执行 * @param key * @return */ public Bitmap getBitmapFromDiskCache(String key){ return getBitmapFromDiskCache(key,null); } public Bitmap getBitmapFromDiskCache(String key,BitmapDisplayConfig config){ return mBitmapProcess.getFromDisk(key, config); } public void setExitTasksEarly(boolean exitTasksEarly) { mExitTasksEarly = exitTasksEarly; } /** * activity onResume的时候调用这个方法,让加载图片线程继续 */ public void onResume(){ setExitTasksEarly(false); } /** * activity onPause的时候调用这个方法,让线程暂停 */ public void onPause() { setExitTasksEarly(true); } /** * activity onDestroy的时候调用这个方法,释放缓存 * 执行过此方法后,FinalBitmap的缓存已经失效,建议通过FinalBitmap.create()获取新的实例 * * @author fantouch */ public void onDestroy() { closeCache(); } /** * 清除所有缓存(磁盘和内存) */ public void clearCache() { new CacheExecutecTask().execute(CacheExecutecTask.MESSAGE_CLEAR); } /** * 根据key清除指定的内存缓存 * @param key */ public void clearCache(String key) { new CacheExecutecTask().execute(CacheExecutecTask.MESSAGE_CLEAR_KEY,key); } /** * 清除缓存 */ public void clearMemoryCache() { if(mImageCache!=null) mImageCache.clearMemoryCache(); } /** * 根据key清除指定的内存缓存 * @param key */ public void clearMemoryCache(String key) { if(mImageCache!=null) mImageCache.clearMemoryCache(key); } /** * 清除磁盘缓存 */ public void clearDiskCache() { new CacheExecutecTask().execute(CacheExecutecTask.MESSAGE_CLEAR_DISK); } /** * 根据key清除指定的内存缓存 * @param key */ public void clearDiskCache(String key) { new CacheExecutecTask().execute(CacheExecutecTask.MESSAGE_CLEAR_KEY_IN_DISK,key); } /** * 关闭缓存 * 执行过此方法后,FinalBitmap的缓存已经失效,建议通过FinalBitmap.create()获取新的实例 * @author fantouch */ public void closeCache() { new CacheExecutecTask().execute(CacheExecutecTask.MESSAGE_CLOSE); } /** * 退出正在加载的线程,程序退出的时候调用词方法 * @param exitTasksEarly */ public void exitTasksEarly(boolean exitTasksEarly) { mExitTasksEarly = exitTasksEarly; if(exitTasksEarly) pauseWork(false);//让暂停的线程结束 } /** * 暂停正在加载的线程,监听listview或者gridview正在滑动的时候条用词方法 * @param pauseWork true停止暂停线程,false继续线程 */ public void pauseWork(boolean pauseWork) { synchronized (mPauseWorkLock) { mPauseWork = pauseWork; if (!mPauseWork) { mPauseWorkLock.notifyAll(); } } } private static BitmapLoadAndDisplayTask getBitmapTaskFromImageView(View imageView) { if (imageView != null) { Drawable drawable = null; if(imageView instanceof ImageView){ drawable = ((ImageView)imageView).getDrawable(); }else{ drawable = imageView.getBackground(); } if (drawable instanceof AsyncDrawable) { final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null; } /** * 检测 imageView中是否已经有线程在运行 * @param data * @param imageView * @return true 没有 false 有线程在运行了 */ public static boolean checkImageTask(Object data, View imageView) { final BitmapLoadAndDisplayTask bitmapWorkerTask = getBitmapTaskFromImageView(imageView); if (bitmapWorkerTask != null) { final Object bitmapData = bitmapWorkerTask.data; if (bitmapData == null || !bitmapData.equals(data)) { bitmapWorkerTask.cancel(true); } else { // 同一个线程已经在执行 return false; } } return true; } public static class AsyncDrawable extends BitmapDrawable { private final WeakReference<BitmapLoadAndDisplayTask> bitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap,BitmapLoadAndDisplayTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference<BitmapLoadAndDisplayTask>( bitmapWorkerTask); } public BitmapLoadAndDisplayTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } } private class CacheExecutecTask extends AsyncTask<Object, Void, Void> { public static final int MESSAGE_CLEAR = 1; public static final int MESSAGE_CLOSE = 2; public static final int MESSAGE_CLEAR_DISK = 3; public static final int MESSAGE_CLEAR_KEY = 4; public static final int MESSAGE_CLEAR_KEY_IN_DISK = 5; @Override protected Void doInBackground(Object... params) { switch ((Integer) params[0]) { case MESSAGE_CLEAR: clearCacheInternalInBackgroud(); break; case MESSAGE_CLOSE: closeCacheInternalInBackgroud(); break; case MESSAGE_CLEAR_DISK: clearDiskCacheInBackgroud(); break; case MESSAGE_CLEAR_KEY: clearCacheInBackgroud(String.valueOf(params[1])); break; case MESSAGE_CLEAR_KEY_IN_DISK: clearDiskCacheInBackgroud(String.valueOf(params[1])); break; } return null; } } /** * bitmap下载显示的线程 * @author michael yang */ private class BitmapLoadAndDisplayTask extends AsyncTask<Object, Void, Bitmap> { private Object data; private final WeakReference<View> imageViewReference; private final BitmapDisplayConfig displayConfig; public BitmapLoadAndDisplayTask(View imageView,BitmapDisplayConfig config) { imageViewReference = new WeakReference<View>(imageView); displayConfig = config; } @Override protected Bitmap doInBackground(Object... params) { data = params[0]; final String dataString = String.valueOf(data); Bitmap bitmap = null; synchronized (mPauseWorkLock) { while (mPauseWork && !isCancelled()) { try { mPauseWorkLock.wait(); } catch (InterruptedException e) { } } } if (bitmap == null && !isCancelled()&& getAttachedImageView() != null && !mExitTasksEarly) { bitmap = processBitmap(dataString,displayConfig); } if(bitmap!=null){ mImageCache.addToMemoryCache(dataString, bitmap); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled() || mExitTasksEarly) { bitmap = null; } // 判断线程和当前的imageview是否是匹配 final View imageView = getAttachedImageView(); if (bitmap != null && imageView != null) { mConfig.displayer.loadCompletedisplay(imageView,bitmap,displayConfig); }else if(bitmap == null && imageView!=null ){ mConfig.displayer.loadFailDisplay(imageView, displayConfig.getLoadfailBitmap()); } } @Override protected void onCancelled(Bitmap bitmap) { super.onCancelled(bitmap); synchronized (mPauseWorkLock) { mPauseWorkLock.notifyAll(); } } /** * 获取线程匹配的imageView,防止出现闪动的现象 * @return */ private View getAttachedImageView() { final View imageView = imageViewReference.get(); final BitmapLoadAndDisplayTask bitmapWorkerTask = getBitmapTaskFromImageView(imageView); if (this == bitmapWorkerTask) { return imageView; } return null; } } /** * @title 配置信息 * @description FinalBitmap的配置信息 * @company 探索者网络工作室(www.tsz.net) * @author michael Young (www.YangFuhai.com) * @version 1.0 * @created 2012-10-28 */ private class FinalBitmapConfig { public String cachePath; public Displayer displayer; public Downloader downloader; public BitmapDisplayConfig defaultDisplayConfig; public float memCacheSizePercent;//缓存百分比,android系统分配给每个apk内存的大小 public int memCacheSize;//内存缓存百分比 public int diskCacheSize;//磁盘百分比 public int poolSize = 3;//默认的线程池线程并发数量 public boolean recycleImmediately = true;//是否立即回收内存 public FinalBitmapConfig(Context context) { defaultDisplayConfig = new BitmapDisplayConfig(); defaultDisplayConfig.setAnimation(null); defaultDisplayConfig.setAnimationType(BitmapDisplayConfig.AnimationType.fadeIn); //设置图片的显示最大尺寸(为屏幕的大小,默认为屏幕宽度的1/2) DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); int defaultWidth = (int)Math.floor(displayMetrics.widthPixels/2); defaultDisplayConfig.setBitmapHeight(defaultWidth); defaultDisplayConfig.setBitmapWidth(defaultWidth); } } }