1、优先从内存中加载图片, 速度最快, 不浪费流量
2、其次从本地(sdcard)加载图片, 速度快, 不浪费流量
3、最后从网络下载图片, 速度慢, 浪费流量
/** * 自定义三级缓存图片加载工具 * */ public class MyBitmapUtils { private NetCacheUtils mNetCacheUtils;//网络缓存工具类 private LocalCacheUtils mLocalCacheUtils;//本地缓存工具类 private MemoryCacheUtils mMemoryCacheUtils; public MyBitmapUtils() { mMemoryCacheUtils = new MemoryCacheUtils(); mLocalCacheUtils = new LocalCacheUtils(); mNetCacheUtils = new NetCacheUtils(mLocalCacheUtils, mMemoryCacheUtils); } public void display(ImageView imageView, String url) { // 设置默认图片 imageView.setImageResource(R.drawable.pic_item_list_default); // 优先从内存中加载图片, 速度最快, 不浪费流量 Bitmap bitmap = mMemoryCacheUtils.getMemoryCache(url); if (bitmap != null) { imageView.setImageBitmap(bitmap); System.out.println("从内存加载图片啦"); return; } // 其次从本地(sdcard)加载图片, 速度快, 不浪费流量 bitmap = mLocalCacheUtils.getLocalCache(url); if (bitmap != null) { imageView.setImageBitmap(bitmap); System.out.println("从本地加载图片啦"); // 写内存缓存 mMemoryCacheUtils.setMemoryCache(url, bitmap); return; } // 最后从网络下载图片, 速度慢, 浪费流量 mNetCacheUtils.getBitmapFromNet(imageView, url); } }
/** * 网络缓存 * */ public class NetCacheUtils { private LocalCacheUtils mLocalCacheUtils; private MemoryCacheUtils mMemoryCacheUtils; public NetCacheUtils(LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) { mLocalCacheUtils = localCacheUtils; mMemoryCacheUtils = memoryCacheUtils; } public void getBitmapFromNet(ImageView imageView, String url) { // AsyncTask 异步封装的工具, 可以实现异步请求及主界面更新(对线程池+handler的封装) new BitmapTask().execute(imageView, url);// 启动AsyncTask } /** * 三个泛型意义: 第一个泛型:doInBackground里的参数类型 * 第二个泛型: onProgressUpdate里的参数类型 * 第三个泛型: onPostExecute里的参数类型及doInBackground的返回类型 * */ class BitmapTask extends AsyncTask<Object, Integer, Bitmap> { private ImageView imageView; private String url; // 1.预加载, 运行在主线程 @Override protected void onPreExecute() { super.onPreExecute(); // System.out.println("onPreExecute"); } // 2.正在加载, 运行在子线程(核心方法), 可以直接异步请求 @Override protected Bitmap doInBackground(Object... params) { // System.out.println("doInBackground"); imageView = (ImageView) params[0]; url = (String) params[1]; imageView.setTag(url);// 打标记, 将当前imageview和url绑定在了一起 // 开始下载图片 Bitmap bitmap = download(url); // publishProgress(values) 调用此方法实现进度更新(会回调onProgressUpdate) return bitmap; } // 3.更新进度的方法, 运行在主线程 @Override protected void onProgressUpdate(Integer... values) { // 更新进度条 super.onProgressUpdate(values); } // 4.加载结束, 运行在主线程(核心方法), 可以直接更新UI @Override protected void onPostExecute(Bitmap result) { // System.out.println("onPostExecute"); if (result != null) { // 给imageView设置图片 // 由于listview的重用机制导致imageview对象可能被多个item共用, // 从而可能将错误的图片设置给了imageView对象 // 所以需要在此处校验, 判断是否是正确的图片 String url = (String) imageView.getTag(); if (url.equals(this.url)) {// 判断图片绑定的url是否就是当前bitmap的url // 如果是,说明图片正确 imageView.setImageBitmap(result); System.out.println("从网络加载图片啦!!!"); // 写本地缓存 mLocalCacheUtils.setLocalCache(url, result); // 写内存缓存 mMemoryCacheUtils.setMemoryCache(url, result); } } super.onPostExecute(result); } } // 下载图片 public Bitmap download(String url) { HttpURLConnection conn = null; try { conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000);// 连接超时 conn.setReadTimeout(5000);// 读取超时 conn.connect(); int responseCode = conn.getResponseCode(); if (responseCode == 200) { InputStream inputStream = conn.getInputStream(); // 根据输入流生成bitmap对象 Bitmap bitmap = BitmapFactory.decodeStream(inputStream); return bitmap; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } } return null; } }LocalCacheUtils.java 本地缓存类
/** * 本地缓存 */ public class LocalCacheUtils { private static final String LOCAL_CACHE_PATH = Environment .getExternalStorageDirectory().getAbsolutePath() + "/image_cache"; // 写本地缓存 public void setLocalCache(String url, Bitmap bitmap) { File dir = new File(LOCAL_CACHE_PATH); if (!dir.exists() || !dir.isDirectory()) { dir.mkdirs();// 创建文件夹 } try { String fileName = MD5Encoder.encode(url); File cacheFile = new File(dir, fileName); bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream( cacheFile));// 参数1:图片格式;参数2:压缩比例0-100; 参数3:输出流 } catch (Exception e) { e.printStackTrace(); } } // 读本地缓存 public Bitmap getLocalCache(String url) { try { File cacheFile = new File(LOCAL_CACHE_PATH, MD5Encoder.encode(url)); if (cacheFile.exists()) { Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream( cacheFile)); return bitmap; } } catch (Exception e) { e.printStackTrace(); } return null; } }MemoryCacheUtils.java 内存缓存类
/** * 内存缓存 */ public class MemoryCacheUtils { private HashMap<String, Bitmap> mMemoryCache = new HashMap<String,Bitmap>(); /** * 写缓存 */ public void setMemoryCache(String url, Bitmap bitmap) { mMemoryCache.put(url, bitmap); } /** * 读缓存 */ public Bitmap getMemoryCache(String url) { return mMemoryCache.get(url); } }但是由于不管android设备总内存是多大, 都只给每个app分配一定内存大小, 16M, 一旦超出16M就内存溢出了。所以内存缓存图片数量过多就会OOM。于是尝试采用软引用SoftReference修改内存缓存类,修改如下:
/** * 内存缓存 */ public class MemoryCacheUtils { <span style="white-space:pre"> </span>private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<String, SoftReference<Bitmap>>(); /** * 写缓存 */ public void setMemoryCache(String url, Bitmap bitmap) { SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);// // 使用软引用将bitmap包装起来 mMemoryCache.put(url, soft); } /** * 读缓存 */ public Bitmap getMemoryCache(String url) { SoftReference<Bitmap> softReference = mMemoryCache.get(url); if (softReference != null) { Bitmap bitmap = softReference.get(); return bitmap; } return null; } }
下面这个是官方文档的截图:
所以再次修改喽~
修改如下:
/** * 内存缓存 * 因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让
* 软引用和弱引用变得不再可靠。Google建议使用LruCache */ public class MemoryCacheUtils { // private HashMap<String, Bitmap> mMemoryCache = new HashMap<String, // Bitmap>(); // private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new // HashMap<String, SoftReference<Bitmap>>(); private LruCache<String, Bitmap> mMemoryCache; public MemoryCacheUtils() { // LruCache 可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围 // Lru: least recentlly used 最近最少使用算法 long maxMemory = Runtime.getRuntime().maxMemory();// 获取分配给app的内存大小 System.out.println("maxMemory:" + maxMemory); mMemoryCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) { // 返回每个对象的大小 @Override protected int sizeOf(String key, Bitmap value) { // int byteCount = value.getByteCount(); int byteCount = value.getRowBytes() * value.getHeight();// 计算图片大小:每行字节数*高度 return byteCount; } }; } /** * 写缓存 */ public void setMemoryCache(String url, Bitmap bitmap) { // mMemoryCache.put(url, bitmap); // SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);// // 使用软引用将bitmap包装起来 // mMemoryCache.put(url, soft); mMemoryCache.put(url, bitmap); } /** * 读缓存 */ public Bitmap getMemoryCache(String url) { // SoftReference<Bitmap> softReference = mMemoryCache.get(url); // // if (softReference != null) { // Bitmap bitmap = softReference.get(); // return bitmap; // } return mMemoryCache.get(url); } }