之前在郭大神的博客看到使用LruCache算法实现图片缓存的.这里仿效他的思路,自己也写了一个. 并加入ConcurrentHashMap<String, SoftReference<Bitmap>>去实现二级缓存,因为ConcurrentHashMap是多个锁的线程安全,支持高并发.很适合这种频繁访问读取内存的操作.
下面整个思路是,使用了系统提供的LruCache类做一级缓存, 大小为运行内存的1/8,当LruCache容量要满的时候,会自动将系统移除的图片放到二级缓存中,但为了避免OOM的问题,这里将SoftReference软引用加入来,当系统快要OOM的时候会自动清除里面的图片内存,当然内存充足时就会继续保存这些二级缓存的图片.强调一点,不要用SoftReference去做一级缓存,现在的java中垃圾回收加强了对SoftReference软引用的回收机制,它只适合临时的保存一些数据缓存,并不适合长期的(相对临时而言,并不是真正的长期).
直接上代码,拿来即用哦:
/** * Created on 3/11/2015 * <br>图片异步加载工具(支持本地图片加载,网络图片URL和项目内图片资源加载) * <br>支持双缓存: LruCache和SoftReference * @author Mr.Et * */ public class ImageLoadManager { /** 图片源类型: 文件,网络,资源ID **/ public enum IMAGE_LOAD_TYPE { FILE_PATH,FILE_URL,FILE_RESOURCE_ID } private String TAG = "ImageLoadManager..."; private Context context; private Set<ImageLoadTask> taskCollection; /** 最大内存 **/ final static int maxCacheSize = (int)(Runtime.getRuntime().maxMemory() / 8); /** 建立线程安全,支持高并发的容器 **/ private static ConcurrentHashMap<String, SoftReference<Bitmap>> currentHashmap = new ConcurrentHashMap<String, SoftReference<Bitmap>>(); public ImageLoadManager(Context context) { super(); this.context = context; taskCollection = new HashSet<ImageLoadManager.ImageLoadTask>(); } private static LruCache<String, Bitmap> BitmapMemoryCache = new LruCache<String, Bitmap>(maxCacheSize) { @Override protected int sizeOf(String key, Bitmap value) { if(value != null) { return value.getByteCount(); //return value.getRowBytes() * value.getHeight(); //旧版本的方法 } else { return 0; } } //这个方法当LruCache的内存容量满的时候会调用,将oldValue的元素移除出来腾出空间给新的元素加入 @Override protected void entryRemoved(boolean evicted, String key,Bitmap oldValue, Bitmap newValue) { if(oldValue != null) { // 当硬引用缓存容量已满时,会使用LRU算法将最近没有被使用的图片转入软引用缓存 currentHashmap.put(key, new SoftReference<Bitmap>(oldValue)); } } }; /** * 针对提供图片资源ID来显示图片的方法 * @param loadType 图片加载类型 * @param imageResourceID 图片资源id * @param imageView 显示图片的ImageView */ public void setImageView(IMAGE_LOAD_TYPE loadType, int imageResourceID, ImageView imageView) { if(loadType == IMAGE_LOAD_TYPE.FILE_RESOURCE_ID) { // if(ifResourceIdExist(imageResourceID)) // { // imageView.setImageResource(imageResourceID); // // }else{ //映射无法获取该图片,则显示默认图片 // imageView.setImageResource(R.drawable.pic_default); // } try { imageView.setImageResource(imageResourceID); return; } catch (Exception e) { Log.e(TAG, "Can find the imageID of "+imageResourceID); e.printStackTrace(); } //默认图片 imageView.setImageResource(R.drawable.pic_default); } } /** * 针对提供图片文件链接或下载链接来显示图片的方法 * @param loadType 图片加载类型 * @param imageFilePath 图片文件的本地文件地址或网络URL的下载链接 * @param imageView 显示图片的ImageView */ public void setImageView(IMAGE_LOAD_TYPE loadType, String imageFilePath, ImageView imageView) { if(imageFilePath == null || imageFilePath.trim().equals("")) { imageView.setImageResource(R.drawable.pic_default); }else{ Bitmap bitmap = getBitmapFromMemoryCache(imageFilePath); if(bitmap != null) { imageView.setImageBitmap(bitmap); } else { imageView.setImageResource(R.drawable.pic_default); ImageLoadTask task = new ImageLoadTask(loadType, imageView); taskCollection.add(task); task.execute(imageFilePath); } } } /** * 从LruCache中获取一张图片,如果不存在就返回null * @param key 键值可以是图片文件的filePath,可以是图片URL地址 * @return Bitmap对象,或者null */ public Bitmap getBitmapFromMemoryCache(String key) { try { if(BitmapMemoryCache.get(key) == null) { if(currentHashmap.get(key) != null) { return currentHashmap.get(key).get(); } } return BitmapMemoryCache.get(key); } catch (Exception e) { e.printStackTrace(); } return BitmapMemoryCache.get(key); } /** * 将图片放入缓存 * @param key * @param bitmap */ private void addBitmapToCache(String key, Bitmap bitmap) { BitmapMemoryCache.put(key, bitmap); } /** * 图片异步加载 * @author Mr.Et * */ private class ImageLoadTask extends AsyncTask<String, Void, Bitmap> { private String imagePath; private ImageView imageView; private IMAGE_LOAD_TYPE loadType; public ImageLoadTask(IMAGE_LOAD_TYPE loadType , ImageView imageView) { this.loadType = loadType; this.imageView = imageView; } @Override protected Bitmap doInBackground(String...params) { imagePath = params[0]; try { if(loadType == IMAGE_LOAD_TYPE.FILE_PATH) { if(new File(imagePath).exists()) { //从本地FILE读取图片 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 2; Bitmap bitmap = BitmapFactory.decodeFile(imagePath, opts); //将获取的新图片放入缓存 addBitmapToCache(imagePath, bitmap); return bitmap; } return null; } else if(loadType == IMAGE_LOAD_TYPE.FILE_URL) { //从网络下载图片 byte[] datas = getBytesOfBitMap(imagePath); if(datas != null) { // BitmapFactory.Options opts = new BitmapFactory.Options(); // opts.inSampleSize = 2; // Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length, opts); Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length); addBitmapToCache(imagePath, bitmap); return bitmap; } return null; } } catch (Exception e) { e.printStackTrace(); FileUtils.saveExceptionLog(e); //可自定义其他操作 } return null; } @Override protected void onPostExecute(Bitmap bitmap) { try { if(imageView != null) { if(bitmap != null) { imageView.setImageBitmap(bitmap); } else { Log.e(TAG, "The bitmap result is null..."); } } else { Log.e(TAG, "The imageView is null..."); //获取图片失败时显示默认图片 imageView.setImageResource(R.drawable.pic_default); } } catch (Exception e) { e.printStackTrace(); FileUtils.saveExceptionLog(e); } } } /** * InputStream转byte[] * @param inStream * @return * @throws Exception */ private byte[] readStream(InputStream inStream) throws Exception{ ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; int len = 0; while( (len=inStream.read(buffer)) != -1){ outStream.write(buffer, 0, len); } outStream.close(); inStream.close(); return outStream.toByteArray(); } /** * 获取下载图片并转为byte[] * @param urlStr * @return */ private byte[] getBytesOfBitMap(String imgUrl){ try { URL url = new URL(imgUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(10 * 1000); //10s conn.setReadTimeout(20 * 1000); conn.setRequestMethod("GET"); conn.connect(); InputStream in = conn.getInputStream(); return readStream(in); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 该资源ID是否有效 * @param resourceId 资源ID * @return */ private boolean ifResourceIdExist(int resourceId) { try { Field field = R.drawable.class.getField(String.valueOf(resourceId)); Integer.parseInt(field.get(null).toString()); return true; } catch (Exception e) { e.printStackTrace(); } return false; } /** * 取消所有任务 */ public void cancelAllTask() { if(taskCollection != null){ for(ImageLoadTask task : taskCollection) { task.cancel(false); } } } }
Android性能优化之实现双缓存的图片异步加载工具(LruCache+SoftReference) - 拿来即用
原文地址:http://blog.csdn.net/stzy00/article/details/44187599