## 三级缓存 ## - 内存缓存, 优先加载, 速度最快 - 本地缓存(内存卡), 次优先加载, 速度快 - 网络缓存, 不优先加载, 速度慢,浪费流量
package com.itheima.zhbj52.utils.bitmap; import android.graphics.Bitmap; import android.widget.ImageView; import com.itheima.zhbj52.R; /** * 自定义图片加载工具(源码仿写。实际开发还是要用BitmapUtils,因为图片的缓存比较难) */ public class MyBitmapUtils { NetCacheUtils mNetCacheUtils; LocalCacheUtils mLocalCacheUtils; MemoryCacheUtils mMemoryCacheUtils; public MyBitmapUtils() { mMemoryCacheUtils = new MemoryCacheUtils(); mLocalCacheUtils = new LocalCacheUtils(); mNetCacheUtils = new NetCacheUtils(mLocalCacheUtils, mMemoryCacheUtils); } //优先加载内存缓存,然后本地缓存,然后网络缓存。 public void display(ImageView ivPic, String url) {//加载图片 ivPic.setImageResource(R.drawable.news_pic_default);// 设置默认加载图片 Bitmap bitmap = null; // 从内存读 bitmap = mMemoryCacheUtils.getBitmapFromMemory(url); if (bitmap != null) { ivPic.setImageBitmap(bitmap); System.out.println("从内存读取图片啦..."); return; } // 从本地读 bitmap = mLocalCacheUtils.getBitmapFromLocal(url); if (bitmap != null) { ivPic.setImageBitmap(bitmap); System.out.println("从本地读取图片啦..."); mMemoryCacheUtils.setBitmapToMemory(url, bitmap);// 将图片保存在内存 return; } // 从网络读 mNetCacheUtils.getBitmapFromNet(ivPic, url); } }
package com.itheima.zhbj52.utils.bitmap; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.widget.ImageView; /** * 网络缓存 */ public class NetCacheUtils { private LocalCacheUtils mLocalCacheUtils; private MemoryCacheUtils mMemoryCacheUtils; public NetCacheUtils(LocalCacheUtils localCacheUtils,MemoryCacheUtils memoryCacheUtils) { mLocalCacheUtils = localCacheUtils; mMemoryCacheUtils = memoryCacheUtils; } /** * 从网络下载图片 */ public void getBitmapFromNet(ImageView ivPic, String url) { new BitmapTask().execute(ivPic, url);// 启动AsyncTask,执行doInBackground,参数会在doInbackground中获取 } /** * AsyncTask是对Handler和线程池(很多线程可以同时运行)的封装,Handler里面的方法是在主线程中执行。 * * 第一个泛型: 参数类型 ,第二个泛型: 更新进度的泛型, 第三个泛型是onPostExecute的返回结果 */ class BitmapTask extends AsyncTask<Object, Void, Bitmap> { private ImageView ivPic; private String url; /** * 后台耗时方法在此执行, 子线程中执行 */ @Override protected Bitmap doInBackground(Object... params) { ivPic = (ImageView) params[0]; url = (String) params[1]; ivPic.setTag(url);// 将url和imageview绑定 return downloadBitmap(url);//下载图片要在子线程执行,下载完后执行onPostExecute方法,返回的bitmap传给result。 } /** * 更新进度, 主线程中执行 */ @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); } /** * 耗时方法结束后,执行该方法, 主线程中执行 */ @Override protected void onPostExecute(Bitmap result) {//result是结果,返回的是下载的图片bitmap if (result != null) { String bindUrl = (String) ivPic.getTag(); if (url.equals(bindUrl)) {// 前面ivPic.setTag(url),这里是确保图片设定给了正确的imageview防止图片错乱。 ivPic.setImageBitmap(result); mLocalCacheUtils.setBitmapToLocal(url, result);// 将图片保存在本地 mMemoryCacheUtils.setBitmapToMemory(url, result);// 将图片保存在内存 System.out.println("从网络缓存读取图片啦..."); } } } } /** * 下载图片 */ private Bitmap downloadBitmap(String url) { HttpURLConnection conn = null; try { conn = (HttpURLConnection) new URL(url).openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.setRequestMethod("GET"); conn.connect(); int responseCode = conn.getResponseCode(); if (responseCode == 200) { InputStream inputStream = conn.getInputStream(); //图片压缩处理() BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = 2;//宽高都压缩为原来的二分之一, 则图片会失真,此参数需要根据图片要展示的大小来确定。 option.inPreferredConfig = Bitmap.Config.RGB_565;//设置图片格式 Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, option); return bitmap; } } catch (Exception e) { e.printStackTrace(); } finally { conn.disconnect(); } return null; } }
package com.itheima.zhbj52.utils.bitmap; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.security.MessageDigest; import com.itheima.zhbj52.utils.MD5Encoder; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.os.Environment; /** * 本地缓存sd卡 */ public class LocalCacheUtils { public static final String CACHE_PATH = Environment .getExternalStorageDirectory().getAbsolutePath() + "/zhbj_cache_52";//根目录下建一个zhbj_cache_52文件夹。 /** * 从本地sdcard读图片 */ public Bitmap getBitmapFromLocal(String url) { try { String fileName = MD5Encoder.encode(url); /*public class MD5Encoder { public static String encode(String string) throws Exception { byte[] hash = MessageDigest.getInstance("MD5").digest( string.getBytes("UTF-8")); StringBuilder hex = new StringBuilder(hash.length * 2); for (byte b : hash) { if ((b & 0xFF) < 0x10) { hex.append("0"); } hex.append(Integer.toHexString(b & 0xFF)); } return hex.toString(); } }*/ File file = new File(CACHE_PATH, fileName); if (file.exists()) { Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream( file));//从文件读取图片。 return bitmap; } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 向sdcard写图片 */ public void setBitmapToLocal(String url, Bitmap bitmap) { try { String fileName = MD5Encoder.encode(url);//url的MD5为文件名, File file = new File(CACHE_PATH, fileName); File parentFile = file.getParentFile();//父文件夹 if (!parentFile.exists()) {// 如果文件夹不存在, 创建文件夹 parentFile.mkdirs(); } // 将图片保存在本地 bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));//CompressFormat.JPEG是压缩格式,100是压缩本质,100%,FileOutputStream是输出流。 } catch (Exception e) { e.printStackTrace(); } } }
package com.itheima.zhbj52.utils.bitmap; import android.graphics.Bitmap; import android.support.v4.util.LruCache; /*## 内存溢出OOM ## - Android默认给每个app只分配16M的内存 - java中的引用 - 强引用 垃圾回收器不会回收, java默认引用都是强引用 - 软引用 SoftReference 在内存不够时,垃圾回收器会考虑回收(即使这个对象有引用存在也会回收) - 弱引用 WeakReference 在内存不够时,垃圾回收器会优先回收 - 虚引用 PhantomReference 在内存不够时,垃圾回收器最优先回收(不建议使用,因为太容易被回收) 注意: Android2.3+, 系统会优先将SoftReference的对象提前回收掉, 即使内存够用,所以弱引用太容易被回收而不再可靠。 ## LruCache(谷歌推荐使用的方法) ## least recentlly use 最少最近使用算法 会将内存控制在一定的大小内, 超出最大值时会自动回收, 这个最大值开发者自己定. ## 图片压缩,否则程序很容易崩溃 ##*/ /** * 内存缓存 */ 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;//LruCache跟HashMap相似采用key,value格式。 public MemoryCacheUtils() { long maxMemory = Runtime.getRuntime().maxMemory() / 8;// 模拟器默认是16M,maxMemory=2M, mMemoryCache = new LruCache<String, Bitmap>((int) maxMemory) { @Override protected int sizeOf(String key, Bitmap value) {//一个Bitmap对象占用的大小。 //图片显示多大就占用多大内存,图片放大时占用内存大图片缩小时占用内存小。 //图片占用内存的大小与图片占用的像素点是相关的,图片显示在屏幕上的时候与图片文件的大小无关只与图片占用的像素点多少有关。 int byteCount = value.getRowBytes() * value.getHeight();// 获取图片占用内存大小,高度*每一行占用的内存大小 return byteCount; } }; } /** * 从内存读 */ public Bitmap getBitmapFromMemory(String url) { /*SoftReference<Bitmap> softReference = mMemoryCache.get(url); if (softReference != null) { Bitmap bitmap = softReference.get();//获取Bitmap对象 return bitmap; }*/ return mMemoryCache.get(url); } /** * 写内存 */ public void setBitmapToMemory(String url, Bitmap bitmap) { // SoftReference<Bitmap> softReference = new SoftReference<Bitmap>(bitmap); // mMemoryCache.put(url, softReference); mMemoryCache.put(url, bitmap);//底层会判断是否超过最大值,超过了就remove第一个。 } }