/**
* 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 cn.coderss.bitmap;
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import cn.coderss.api.FssApi;
import cn.coderss.bean.BBSBean;
import cn.coderss.bean.TestInfo;
import cn.coderss.ui.FlowView;
import cn.coderss.util.L;
public class FinalBitmap {
private FinalBitmapConfig mConfig;
private static BitmapCache mImageCache;
private boolean mExitTasksEarly = false;
private boolean mPauseWork = false;
private final Object mPauseWorkLock = new Object();
private Context mContext;
private static ExecutorService bitmapLoadAndDisplayExecutor;
public interface ImageLoadCompleteListener {
void onLoadComplete(Bitmap bitmap, BBSBean _info);
}
private ImageLoadCompleteListener completeListener;
public FinalBitmap(Context context) {
mContext = context;
mConfig = new FinalBitmapConfig(context);
configDiskCachePath(BitmapCommonUtils.getDiskCacheDir(context,
"afinalCache"));// 配置缓存路径
configDisplayer(new SimpleDisplayer());// 配置显示器
configDownlader(new SimpleHttpDownloader());// 配置下载器
}
/**
* 设置图片正在加载的时候显示的图片
*
* @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 strPath
* @return
*/
public FinalBitmap configDiskCachePath(String strPath) {
if (!TextUtils.isEmpty(strPath)) {
mConfig.cachePath = strPath;
}
return this;
}
/**
* 配置磁盘缓存路径
*
* @param strPath
* @return
*/
public FinalBitmap configDiskCachePath(File pathFile) {
if (pathFile != null)
configDiskCachePath(pathFile.getAbsolutePath());
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;
}
/**
* 配置内存缓存大小 大于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 configOriginalDiskCacheSize(int size) {
mConfig.diskCacheSize = size;
return this;
}
/**
* 设置加载图片的线程并发数量
*
* @param size
*/
public FinalBitmap configBitmapLoadThreadSize(int size) {
if (size >= 1)
mConfig.poolSize = size;
return this;
}
/**
* 这个方法必须被调用后 FinalBitmap 配置才能有效
*
* @return
*/
public FinalBitmap init() {
mConfig.init();
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);
mImageCache = new BitmapCache(imageCacheParams);
bitmapLoadAndDisplayExecutor = Executors.newFixedThreadPool(
mConfig.poolSize, new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
// 设置线程的优先级别,让线程先后顺序执行(级别越高,抢到cpu执行的时间越多)
t.setPriority(Thread.NORM_PRIORITY - 1);
return t;
}
});
new CacheExecutecTask()
.execute(CacheExecutecTask.MESSAGE_INIT_DISK_CACHE);
return this;
}
private boolean _isReload = false;
public void reload(String uri, FlowView view) {
reloadDisplay(uri, null, view);
}
public void display(BBSBean info) {
doDisplay(info.getPicture(), null, info);
}
private void reloadDisplay(String uri, BitmapDisplayConfig displayConfig,
FlowView view) {
if (TextUtils.isEmpty(uri)) {
return;
}
if (displayConfig == null)
displayConfig = mConfig.defaultDisplayConfig;
Bitmap bitmap = null;
if (mImageCache != null) {
bitmap = mImageCache.getBitmapFromMemCache(uri);
}
if (bitmap != null) {
view.setImageBitmap(bitmap);
} else {
final BitmapReoadTask task = new BitmapReoadTask(displayConfig,
view);
task.executeOnExecutor(bitmapLoadAndDisplayExecutor, uri);
}
}
private void doDisplay(String uri, BitmapDisplayConfig displayConfig,
BBSBean info) {
Log.d("FinalBitmap", "currentUrl:" + uri + ",is reload:" + _isReload);
if (TextUtils.isEmpty(uri)) {
return;
}
if (displayConfig == null)
displayConfig = mConfig.defaultDisplayConfig;
Bitmap bitmap = null;
if (mImageCache != null) {
bitmap = mImageCache.getBitmapFromMemCache(FssApi.AVATOR
+ info.getUsername() + "/" + uri);
L.i("要载入的图片" + FssApi.AVATOR + info.getUsername() + "/" + uri);
}
if (bitmap != null) {
getCompleteListener().onLoadComplete(bitmap, info);
} else {
final BitmapLoadTask task = new BitmapLoadTask(displayConfig, info);
task.executeOnExecutor(bitmapLoadAndDisplayExecutor, uri);
}
}
private void initDiskCacheInternal() {
if (mImageCache != null) {
mImageCache.initDiskCache();
}
if (mConfig != null && mConfig.bitmapProcess != null) {
mConfig.bitmapProcess.initHttpDiskCache();
}
}
private void clearCacheInternal() {
if (mImageCache != null) {
mImageCache.clearCache();
}
if (mConfig != null && mConfig.bitmapProcess != null) {
mConfig.bitmapProcess.clearCacheInternal();
}
}
private void flushCacheInternal() {
if (mImageCache != null) {
mImageCache.flush();
}
if (mConfig != null && mConfig.bitmapProcess != null) {
mConfig.bitmapProcess.flushCacheInternal();
}
}
private void closeCacheInternal() {
if (mImageCache != null) {
mImageCache.close();
mImageCache = null;
}
if (mConfig != null && mConfig.bitmapProcess != null) {
mConfig.bitmapProcess.clearCacheInternal();
}
}
/**
* 网络加载bitmap
*
* @param data
* @return
*/
private Bitmap processBitmap(String uri, BitmapDisplayConfig config) {
if (mConfig != null && mConfig.bitmapProcess != null) {
return mConfig.bitmapProcess.processBitmap(uri, config);
}
return null;
}
public void setExitTasksEarly(boolean exitTasksEarly) {
mExitTasksEarly = exitTasksEarly;
}
/**
* activity onResume的时候调用这个方法,让加载图片线程继续
*/
public void onResume() {
setExitTasksEarly(false);
}
/**
* activity onPause的时候调用这个方法,让线程暂停
*/
public void onPause() {
setExitTasksEarly(true);
flushCache();
}
/**
* activity onDestroy的时候调用这个方法,释放缓存
*/
public void onDestroy() {
closeCache();
}
/**
* 清除缓存
*/
public void clearCache() {
new CacheExecutecTask().execute(CacheExecutecTask.MESSAGE_CLEAR);
}
/**
* 刷新缓存
*/
public void flushCache() {
new CacheExecutecTask().execute(CacheExecutecTask.MESSAGE_FLUSH);
}
/**
* 关闭缓存
*/
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();
}
}
}
public ImageLoadCompleteListener getCompleteListener() {
return completeListener;
}
public void setCompleteListener(ImageLoadCompleteListener completeListener) {
this.completeListener = completeListener;
}
/**
* @title 缓存操作的异步任务
* @description 操作缓存
* @company 探索者网络工作室(www.tsz.net)
* @author michael Young (www.YangFuhai.com)
* @version 1.0
* @created 2012-10-28
*/
private class CacheExecutecTask extends AsyncTask<Object, Void, Void> {
public static final int MESSAGE_CLEAR = 0;
public static final int MESSAGE_INIT_DISK_CACHE = 1;
public static final int MESSAGE_FLUSH = 2;
public static final int MESSAGE_CLOSE = 3;
@Override
protected Void doInBackground(Object... params) {
switch ((Integer) params[0]) {
case MESSAGE_CLEAR:
clearCacheInternal();
break;
case MESSAGE_INIT_DISK_CACHE:
initDiskCacheInternal();
break;
case MESSAGE_FLUSH:
flushCacheInternal();
break;
case MESSAGE_CLOSE:
closeCacheInternal();
break;
}
return null;
}
}
/**
* bitmap下载显示的线程
*
* @author michael yang
*/
@SuppressLint("NewApi")
private class BitmapLoadTask extends AsyncTask<Object, Void, Bitmap> {
private Object data;
private final BitmapDisplayConfig displayConfig;
private BBSBean info;
public BitmapLoadTask(BitmapDisplayConfig config, BBSBean _info) {
displayConfig = config;
info = _info;
}
@Override
protected Bitmap doInBackground(Object... params) {
data = FssApi.AVATOR + info.getUsername() + "/" + params[0];
final String dataString = String.valueOf(data);
Bitmap bitmap = null;
L.i("我要下载的图片地址:" + data);
synchronized (mPauseWorkLock) {
while (mPauseWork && !isCancelled()) {
try {
mPauseWorkLock.wait();
} catch (InterruptedException e) {
}
}
}
flushCache();
if (mImageCache != null && !isCancelled() && !mExitTasksEarly) {
bitmap = mImageCache.getBitmapFromDiskCache(dataString);
}
if (bitmap == null && !isCancelled() && !mExitTasksEarly) {
bitmap = processBitmap(dataString, displayConfig);
}
if (bitmap != null && mImageCache != null) {
mImageCache.addBitmapToCache(dataString, bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
// Log.d("FinalBitmap", "onPostExecute");
if (isCancelled() || mExitTasksEarly) {
bitmap = null;
}
// 判断线程和当前的imageview是否是匹配
if (bitmap != null) {
getCompleteListener().onLoadComplete(bitmap, info);
} else if (bitmap == null) {
}
}
@Override
protected void onCancelled(Bitmap bitmap) {
super.onCancelled(bitmap);
synchronized (mPauseWorkLock) {
mPauseWorkLock.notifyAll();
}
}
}
private class BitmapReoadTask extends AsyncTask<Object, Void, Bitmap> {
private Object data;
private final BitmapDisplayConfig displayConfig;
private FlowView view;
public BitmapReoadTask(BitmapDisplayConfig config, FlowView _view) {
displayConfig = config;
view = _view;
}
@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 (mImageCache != null && !isCancelled() && !mExitTasksEarly) {
bitmap = mImageCache.getBitmapFromDiskCache(dataString);
}
if (bitmap == null && !isCancelled() && !mExitTasksEarly) {
bitmap = processBitmap(dataString, displayConfig);
}
if (bitmap != null && mImageCache != null) {
mImageCache.addBitmapToCache(dataString, bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
// Log.d("FinalBitmap", "onPostExecute");
if (isCancelled() || mExitTasksEarly) {
bitmap = null;
}
// 判断线程和当前的imageview是否是匹配
if (bitmap != null) {
view.setImageBitmap(bitmap);
} else if (bitmap == null) {
}
}
@SuppressLint("NewApi")
@Override
protected void onCancelled(Bitmap bitmap) {
super.onCancelled(bitmap);
synchronized (mPauseWorkLock) {
mPauseWorkLock.notifyAll();
}
}
}
/**
* @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 BitmapProcess bitmapProcess;
public BitmapDisplayConfig defaultDisplayConfig;
public float memCacheSizePercent;// 缓存百分比,android系统分配给每个apk内存的大小
public int memCacheSize;// 内存缓存百分比
public int diskCacheSize;// 磁盘百分比
public int poolSize = 3;// 默认的线程池线程并发数量
public int originalDiskCache = 20 * 1024 * 1024;// 20MB
public FinalBitmapConfig(Context context) {
defaultDisplayConfig = new BitmapDisplayConfig();
defaultDisplayConfig.setAnimation(null);
defaultDisplayConfig
.setAnimationType(BitmapDisplayConfig.AnimationType.fadeIn);
// 设置图片的显示最大尺寸(为屏幕的大小,默认为屏幕宽度的1/3)
DisplayMetrics displayMetrics = context.getResources()
.getDisplayMetrics();
int defaultWidth = (int) Math.floor(displayMetrics.widthPixels / 3);
defaultDisplayConfig.setBitmapHeight(defaultWidth);
defaultDisplayConfig.setBitmapWidth(defaultWidth);
}
public void init() {
if (downloader == null)
downloader = new SimpleHttpDownloader();
if (displayer == null)
displayer = new SimpleDisplayer();
bitmapProcess = new BitmapProcess(downloader, cachePath,
originalDiskCache);
}
}
}