三级缓存:首先是内存-文件(外存)-网络三级缓存机制。
首先:
框架需要一个接入方法NGImageloadHelper.Java:
/**
* 图片加载框架使用帮助类
* Created by nangua on 2016/7/8.
*/
public class NGImageloadHelper {
/**
* 处理图片
* @param view
* @param url
*/
public static void displayImage(ImageView view,
String url) {
NGDownloadImage.getInstance().addTask(url,
view);
NGDownloadImage.getInstance().doTask();
}
}
然后,使用具体的缓存实现类NGDownloadImage:
首先判断传入的url对应图片是否在内外存中,如果不在,则添加进线程池的自定义任务队列中,这里传入的任务是自定义的实现Callble接口的任务TaskWithResult,在带回调参数的执行方法call中执行一个自定义的handler—-TaskHandler,该TaskHandler的handlerMessage方法内部根据传入的图片类型判断,执行相应的下载方法(通过HttpUrlConnection实现)并移除taskmap中对应的图片任务。
/**
* 图片加载类
* Created by nangua on 2016/7/8.
*/
public class NGDownloadImage {
private ExecutorService executorService; //线程池服务
private NGImageMemoryCache imageMemoryCache;
private NGImageFileCache imageFileCache;
private NGDownloadImageMode downloadImageMode; //图片实例
private Map<String, View> taskMap;
private static NGDownloadImage instance; //自身私有化实例
private int POOL_SIZE = 5;//线程池自定义大小
private NGDownloadImage() {
final int cpuNums = Runtime.getRuntime().availableProcessors();//cpu数
executorService = Executors.newFixedThreadPool(cpuNums * POOL_SIZE);
imageMemoryCache = new NGImageMemoryCache();
imageFileCache = new NGImageFileCache();
downloadImageMode = new NGDownloadImageMode();
taskMap = new HashMap<>();
}
//获得唯一实例
public static synchronized NGDownloadImage getInstance() {
if (instance == null) {
instance = new NGDownloadImage();
}
return instance;
}
/**
* 添加任务
*
* @param url
* @param img
*/
public void addTask(String url, ImageView img) {
addTask(null, url, img, null);
}
public void addTask(Object parent, String url, View img,
NGImageCallback callback) {
if (img == null) {
return;
}
if (TextUtils.isEmpty(url)) {
return;
}
if (callback != null) {
downloadImageMode = new NGDownloadImageMode();
downloadImageMode.setCallback(callback);
downloadImageMode.setParent(parent);
downloadImageMode.setImgUrl(url);
img.setTag(downloadImageMode);
} else {
img.setTag(url);
}
//生成Bitmap
final Bitmap bitmap = imageMemoryCache.getBitmapFromCache(url);
//如果缓存里有
if (bitmap != null) {
//如果有实现的回调接口,则用回调接口加载图片
if (callback != null) {
callback.imageLoaded(parent, img, bitmap, downloadImageMode);
} else {
//如果没有,则直接设置该图片为bitmap
if (img instanceof ImageView)
((ImageView) img).setImageBitmap(bitmap);
}
} else {
//如果缓存没有这个图片
if (taskMap != null) {
//添加到任务集合里去
synchronized (taskMap) {
final String mapKey = Integer.toString(img.hashCode());
if (!taskMap.containsKey(mapKey)) {
taskMap.put(mapKey, img);
}
}
}
}
}
public void doTask() {
if (taskMap == null) {
return;
} else {
synchronized (taskMap) {
Collection<View> collection = taskMap.values();
for (View view : collection) {
if (view != null) {
Object object = view.getTag();
String url = "";
if (object instanceof NGDownloadImageMode) {
url = ((NGDownloadImageMode) object).getImgUrl();
} else {
url = (String) object;
}
if (!TextUtils.isEmpty(url)) {
loadImage(url, view);
}
}
}
}
}
}
private void loadImage(final String url, final View img) {
loadImage(url, img, null);
}
private void loadImage(final String url, final View img,
NGImageCallback callback) {
executorService.submit(new TaskWithResult(new TaskHandler(url, img,
callback), url));
}
private class TaskWithResult implements Callable<String> {
private String url;
private Handler handler;
public TaskWithResult(Handler handler, String url) {
this.url = url;
this.handler = handler;
}
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
final Message message = handler.obtainMessage(0, getBitmap(url));
handler.sendMessage(message);
return url;
}
}
private class TaskHandler extends Handler {
private String url;
private View img;
private NGImageCallback callback;
public TaskHandler(String url, View img, NGImageCallback callback) {
this.url = url;
this.img = img;
this.callback = callback;
}
@Override
public void handleMessage(Message msg) {
final Object object = img.getTag();
if (object instanceof NGDownloadImageMode) {
final NGDownloadImageMode imageMode = (NGDownloadImageMode) object;
imageMode.getCallback().imageLoaded(imageMode.getParent(), img,
(Bitmap) msg.obj, imageMode);
if (taskMap != null) {
taskMap.remove(Integer.toString(img.hashCode()));
}
} else if (object instanceof String) {
if (callback != null) {
callback.imageLoaded(null, img, (Bitmap) msg.obj, url);
} else {
if (object.equals(url) && msg.obj != null) {
final Bitmap bitmap = (Bitmap) msg.obj;
if (bitmap != null) {
if (img instanceof ImageView) {
((ImageView) img).setImageBitmap(bitmap);
}
}
}
}
if (taskMap != null) {
taskMap.remove(Integer.toString(img.hashCode()));
}
}
}
}
/**
* @param url
* @return Bitmap
*/
public Bitmap getBitmap(String url) {
Bitmap bitmap = imageMemoryCache.getBitmapFromCache(url);
if (bitmap == null) {
bitmap = imageFileCache.getImage(url);
if (bitmap == null) {
bitmap = getBitmapFromUrl(url);
if (bitmap != null) {
imageMemoryCache.addBitmapToCache(url, bitmap);
imageFileCache.saveBmpToSd(url,bitmap);
}
} else {
imageMemoryCache.addBitmapToCache(url, bitmap);
}
}
return bitmap;
}
public static Bitmap getBitmapFromUrl(String path) {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (conn.getResponseCode() == 200) {
InputStream inputStream = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public interface NGImageCallback {
public void imageLoaded(Object parent, View img, Bitmap imageBitmap,
NGDownloadImageMode callBackTag);
public void imageLoaded(Object parent, View img, Bitmap imageBitmap,
String imageUrl);
}
}
内存缓存实现:
通过强&软引用配合使用实现内存缓存机制,强引用使用HashMap实现,软引用使用线程安全的 ConcurrentHashMap实现(实现原理是锁分离技术,使用多个锁来控制对hash表的不同部分的修改,内部使用段(Segment)来表示这些不同的部分,每个段是一个小的hashtable,可并发运行),淘汰算法如下:
/**
* 初始化
* 淘汰最老的键
*/
protected NGImageMemoryCache() {
//使用LinkedHashMap保证有序读取
hashMap = new LinkedHashMap<String, Bitmap>(MAX_CACHE_CAPACITY, 0.75f, true) {
//移除hashmap中最老的键值
@Override
protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
if (size() > MAX_CACHE_CAPACITY) {
mSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
return true; //返回true则移除最老的键值
} else {
return false;
}
}
};
}
外存缓存实现:
很简单这里就只讲一下思路了,把图片文件存储到本地指定文件夹中,注意进行剩余容量判断及时清除最老的图片就行了。