详解Android中图片的三级缓存及实例
程序员文章站
2023-12-03 11:40:10
详解android中图片的三级缓存及实例
为什么要使用三级缓存
如今的 android app 经常会需要网络交互,通过网络获取图片是再正常不过的事了...
详解android中图片的三级缓存及实例
为什么要使用三级缓存
- 如今的 android app 经常会需要网络交互,通过网络获取图片是再正常不过的事了
- 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响
- 特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知
- 所以提出三级缓存策略,通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量
什么是三级缓存
- 网络缓存, 不优先加载, 速度慢,浪费流量
- 本地缓存, 次优先加载, 速度快
- 内存缓存, 优先加载, 速度最快
三级缓存原理
- 首次加载 android app 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地sd卡和内存中
- 之后运行 app 时,优先访问内存中的图片缓存,若内存中没有,则加载本地sd卡中的图片
- 总之,只在初次访问新内容时,才通过网络获取图片资源
具体实现及代码
1. 自定义的图片缓存工具类(mybitmaputils)
通过new mybitmaputils().display(imageview ivpic, string url) 提供给外部方法进行图片缓存的接口
参数含义:ivpic 用于显示图片的imageview,url 获取图片的网络地址
/** * 自定义的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 void display(imageview ivpic, string url) { ivpic.setimageresource(r.mipmap.pic_item_list_default); bitmap bitmap; //内存缓存 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); } }
2. 网络缓存(netcacheutils)
- 网络缓存中主要用到了asynctask来进行异步数据的加载
- 简单来说,asynctask可以看作是一个对handler和线程池的封装,通常,asynctask主要用于数据简单时,handler+thread主要用于数据量多且复杂时,当然这也不是必须的,仁者见仁智者见智
- 同时,为了避免内存溢出的问题,我们可以在获取网络图片后。对其进行图片压缩
/** * 三级缓存之网络缓存 */ public class netcacheutils { private localcacheutils mlocalcacheutils; private memorycacheutils mmemorycacheutils; public netcacheutils(localcacheutils localcacheutils, memorycacheutils memorycacheutils) { mlocalcacheutils = localcacheutils; mmemorycacheutils = memorycacheutils; } /** * 从网络下载图片 * @param ivpic 显示图片的imageview * @param url 下载图片的网络地址 */ public void getbitmapfromnet(imageview ivpic, string url) { new bitmaptask().execute(ivpic, url);//启动asynctask } /** * 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); } } } /** * 网络下载图片 * @param url * @return */ private 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); return bitmap; } } catch (ioexception e) { e.printstacktrace(); } finally { conn.disconnect(); } return null; } }
3. 本地缓存(localcacheutils)
- 在初次通过网络获取图片后,我们可以在本地sd卡中将图片保存起来
- 可以使用md5加密图片的网络地址,来作为图片的名称保存
/** * 三级缓存之本地缓存 */ public class localcacheutils { private static final string cache_path= environment.getexternalstoragedirectory().getabsolutepath()+"/werbnews"; /** * 从本地读取图片 * @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(); } } }
4. 内存缓存(memorycacheutils)
- 这是本文中最重要且需要重点介绍的部分
- 进行内存缓存,就一定要注意一个问题,那就是内存溢出(outofmemory)
- 为什么会造成内存溢出?
- android 虚拟机默认分配给每个app 16m的内存空间,真机会比16m大,但任会出现内存溢出的情况
- android 系统在加载图片时是解析每一个像素的信息,再把每一个像素全部保存至内存中
- 图片大小 = 图片的总像素 * 每个像素占用的大小
单色图:每个像素占用1/8个字节,16色图:每个像素占用1/2个字节,256色图:每个像素占用1个字节,24位图:每个像素占用3个字节(常见的rgb构成的图片)
例如一张1920x1080的jpg图片,在android 系统中是以argb格式解析的,即一个像素需占用4个字节,图片的大小=1920x1080x4=7m
实现方法:
- 通过 hashmap<string,bitmap>键值对的方式保存图片,key为地址,value为图片对象,但因是强引用对象,很容易造成内存溢出,可以尝试softreference软引用对象
- 通过 hashmap<string, softreference<bitmap>>softreference 为软引用对象(gc垃圾回收会自动回收软引用对象),但在android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用lrucache
- 通过 lrucache<string,bitmap> least recentlly use 最少最近使用算法会将内存控制在一定的大小内, 超出最大值时会自动回收, 这个最大值开发者自己定
/** * 三级缓存之内存缓存 */ public class memorycacheutils { // private hashmap<string,bitmap> mmemorycache=new hashmap<>();//1.因为强引用,容易造成内存溢出,所以考虑使用下面弱引用的方法 // private hashmap<string, softreference<bitmap>> mmemorycache = new hashmap<>();//2.因为在android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用lrucache 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; } */ 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); } }
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!