1.简介
Android对于图片的优化,一直都有的。第三方的框架imageloader到Picasso、glide、Fresco,各领风骚,但谁也不能独步武林,一统江湖。但是他们核心的缓存原理都是一样,我们也可以自己封装。
三级缓存:
1.内存缓存:缓存在内存中,基于LRU(least recently used )算法,机器重启消失。
2.本地缓存。缓存在本地中。一般键值对形式。(url,filepath)
3.网络缓存。从网络加载资源,然后缓存在内存、本地中。
2.实现步骤
2.1 内存缓存:
public class MemoryCacheUtils { private LruCache<String,Bitmap> mMemoryCache; public MemoryCacheUtils(){ long maxMemory = Runtime.getRuntime().maxMemory()/8;//得到手机最大允许内存的1/8,即超过指定内存,则开始回收 //需要传入允许的内存最大值,虚拟机默认内存16M,真机不一定相同 mMemoryCache=new LruCache<String,Bitmap>((int) maxMemory){ //用于计算每个条目的大小 @Override protected int sizeOf(String key, Bitmap value) { int byteCount = value.getByteCount(); return byteCount; } }; } /** * 从内存中读图片 * @param url */ public Bitmap getBitmapFromMemory(String url) { //Bitmap bitmap = mMemoryCache.get(url);//1.强引用方法 /*2.弱引用方法 SoftReference<Bitmap> bitmapSoftReference = mMemoryCache.get(url); if (bitmapSoftReference != null) { Bitmap bitmap = bitmapSoftReference.get(); return bitmap; } */ if(url==null||"".equals(url)){ return null; } Bitmap bitmap = mMemoryCache.get(url); return bitmap; } /** * 往内存中写图片 * @param url * @param bitmap */ public void setBitmapToMemory(String url, Bitmap bitmap) { //mMemoryCache.put(url, bitmap);//1.强引用方法 /*2.弱引用方法 mMemoryCache.put(url, new SoftReference<>(bitmap)); */ mMemoryCache.put(url,bitmap); } }
2.2本地缓存
public class LocalCacheUtils { private static final String CACHE_PATH= Environment.getExternalStorageDirectory().getAbsolutePath()+"/my/images"; /** * 从本地读取图片 * @param url */ public Bitmap getBitmapFromLocal(String url){ String fileName = null;//把图片的url当做文件名,并进行MD5加密 try { fileName = MD5Encoder.encode(url); //这里加不加密无所谓 File file=new File(CACHE_PATH,fileName); Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file)); return bitmap; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 从网络获取图片后,保存至本地缓存 * @param url * @param bitmap */ 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(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file)); } catch (Exception e) { e.printStackTrace(); } } }
2.3网络缓存
public class NetCacheUtils { private LocalCacheUtils mLocalCacheUtils; private MemoryCacheUtils mMemoryCacheUtils; public NetCacheUtils(LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) { mLocalCacheUtils = localCacheUtils; mMemoryCacheUtils = memoryCacheUtils; } public NetCacheUtils(){ } /** * 从网络下载图片 * @param ivPic 显示图片的imageview * @param url 下载图片的网络地址 */ public void getBitmapFromNet(ImageView ivPic, String url) { new BitmapTask().execute(ivPic, url);//启动AsyncTask } public void getBitmapFromNet(View ivPic, String url) { new BitmapTask_view().execute(ivPic, url);//启动AsyncTask } public Bitmap getBitmapFromNet(final String url) { //启动AsyncTask return null; } /** * AsyncTask就是对handler和线程池的封装 * 第一个泛型:参数类型 * 第二个泛型:更新进度的泛型 * 第三个泛型:onPostExecute的返回结果 */ class BitmapTask extends AsyncTask<Object, Void, Bitmap> { private ImageView ivPic; private String url; /** * 后台耗时操作,存在于子线程中 * @param params * @return */ @Override protected Bitmap doInBackground(Object[] params) { ivPic = (ImageView) params[0]; url = (String) params[1]; return downLoadBitmap(url); } /** * 更新进度,在主线程中 * @param values */ @Override protected void onProgressUpdate(Void[] values) { super.onProgressUpdate(values); } /** * 耗时方法结束后执行该方法,主线程中 * @param result */ @Override protected void onPostExecute(Bitmap result) { if (result != null) { ivPic.setImageBitmap(result); System.out.println("从网络缓存图片啦....."); //从网络获取图片后,保存至本地缓存 mLocalCacheUtils.setBitmapToLocal(url, result); //保存至内存中 mMemoryCacheUtils.setBitmapToMemory(url, result); } } } /** * AsyncTask就是对handler和线程池的封装 * 第一个泛型:参数类型 * 第二个泛型:更新进度的泛型 * 第三个泛型:onPostExecute的返回结果 */ @SuppressLint("NewApi") class BitmapTask_view extends AsyncTask<Object, Void, Bitmap> { private View ivPic; private String url; /** * 后台耗时操作,存在于子线程中 * @param params * @return */ @Override protected Bitmap doInBackground(Object[] params) { ivPic = (View) params[0]; url = (String) params[1]; return downLoadBitmap(url); } /** * 更新进度,在主线程中 * @param values */ @Override protected void onProgressUpdate(Void[] values) { super.onProgressUpdate(values); } /** * 耗时方法结束后执行该方法,主线程中 * @param result */ @Override protected void onPostExecute(Bitmap result) { if (result != null) { //ivPic.setImageBitmap(result); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { //Android系统大于等于API16,使用setBackground ivPic.setBackground(new BitmapDrawable(result)); } else { //Android系统小于API16,使用setBackground ivPic.setBackgroundDrawable(new BitmapDrawable(result)); } System.out.println("从网络缓存图片啦....."); //从网络获取图片后,保存至本地缓存 mLocalCacheUtils.setBitmapToLocal(url, result); //保存至内存中 mMemoryCacheUtils.setBitmapToMemory(url, result); } } } /** * 网络下载图片 * @param url * @return */ public Bitmap downLoadBitmap(String url) { HttpURLConnection conn = null; try { conn = (HttpURLConnection) new URL(url).openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.setRequestMethod("GET"); int responseCode = conn.getResponseCode(); if (responseCode == 200) { //图片压缩 BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize=2;//宽高压缩为原来的1/2 options.inPreferredConfig=Bitmap.Config.ARGB_4444; //Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream(),null,options); Bitmap bitmap=BitmapFactory.decodeStream(conn.getInputStream()); return bitmap; } } catch (IOException e) { e.printStackTrace(); }catch (Exception e) { } finally { if(conn!=null){ conn.disconnect(); } } return null; } }
2.4外部写一个bitmapUtils来调用它们。
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 Bitmap getBitmap(String url){ Bitmap bitmap=null; bitmap=mMemoryCacheUtils.getBitmapFromMemory(url); if(bitmap!=null){ return bitmap; } bitmap = mLocalCacheUtils.getBitmapFromLocal(url); if(bitmap!=null){ mMemoryCacheUtils.setBitmapToMemory(url,bitmap); return bitmap; } return bitmap; } public void disPlay(ImageView ivPic, String url) { Bitmap bitmap; //内存缓存 bitmap=mMemoryCacheUtils.getBitmapFromMemory(url); if (bitmap!=null){ ivPic.setImageBitmap(bitmap); Log.d("iamgecache","从内存获取图片啦.....--->"+url); return; } //本地缓存 bitmap = mLocalCacheUtils.getBitmapFromLocal(url); if(bitmap !=null){ ivPic.setImageBitmap(bitmap); Log.d("iamgecache","从本地获取图片啦.....-->"+url); //从本地获取图片后,保存至内存中 mMemoryCacheUtils.setBitmapToMemory(url,bitmap); return; } //网络缓存 mNetCacheUtils.getBitmapFromNet(ivPic,url); Log.d("iamgecache","从网络获取图片啦.....-->"+url); } @SuppressLint("NewApi") public void disPlay(View ivPic, String url) { Bitmap bitmap; //内存缓存 bitmap=mMemoryCacheUtils.getBitmapFromMemory(url); if (bitmap!=null){ //ivPic.setImageBitmap(bitmap); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { //Android系统大于等于API16,使用setBackground ivPic.setBackground(new BitmapDrawable(bitmap)); } else { //Android系统小于API16,使用setBackground ivPic.setBackgroundDrawable(new BitmapDrawable(bitmap)); } //ivPic.setBackground(new BitmapDrawable(bitmap)); Log.d("iamgecache","从内存获取图片啦.....--->"+url); return; } //本地缓存 bitmap = mLocalCacheUtils.getBitmapFromLocal(url); if(bitmap !=null){ // ivPic.setImageBitmap(bitmap); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { //Android系统大于等于API16,使用setBackground ivPic.setBackground(new BitmapDrawable(bitmap)); } else { //Android系统小于API16,使用setBackground ivPic.setBackgroundDrawable(new BitmapDrawable(bitmap)); } //ivPic.setBackground(new BitmapDrawable(bitmap)); Log.d("iamgecache","从本地获取图片啦.....-->"+url); //从本地获取图片后,保存至内存中 mMemoryCacheUtils.setBitmapToMemory(url,bitmap); return; } //网络缓存 mNetCacheUtils.getBitmapFromNet(ivPic,url); // ivPic.setBackground(new BitmapDrawable(bitmap)); Log.d("iamgecache","从网络获取图片啦.....-->"+url); } }个人封装的网络缓存框架的好处是便于修改,自己清楚流程。适合于一些对图片质量没那么高要求而又需要缓存减少网络访问的情景。