首页 > 其他分享 >Android网络图片三级缓存策略

Android网络图片三级缓存策略

时间:2023-06-04 16:32:10浏览次数:56  
标签:缓存 bitmap ImageLoader Android 图片 DiskLruCache 三级 view


在移动应用中,我们一般将网络图片分为三个级别,第一级别是网络层,即根据图片的url地址可以找到服务器上相应图片,获取这一层的图片会消耗流量,所以我们希望可以获取后本地就永久使用,所以就会有接下来的缓存策略;第二层缓存是在手机内存层,是将第一层的图片下载到手机内存,这种缓存读取速度非常快,但当图片内存被回收时,图片自然就不会存在了,第三层则是在手机硬盘层,是会缓存到sd卡。但这一层相对于内存的读取速度会慢很多,所以,很好的协调这三层图片缓存就可以提升应用性能和用户体验。

秉着不重复造轮子原则,这里我采用Volley+LruCache+DiskLruCache三个谷歌官方认可的库来实现网络图片三级缓存。并且以“one line”风格来实现将网络图片显示在ImageView上,而无需关心任何缓存细节。

类库下载

  1. Volley是Goole在2013年Google I/O大会上推出了一个新的网络通信框架,它是开源的,你可以通过git来clone源码并倒入项目:

git clone https://android.googlesource.com/platform/frameworks/volley

这个地址可能会被和谐,所以你可以在这里下载完整的jar包:
http://cdn.saymagic.cn/150131132336.jar

2.LruCache这个类是Android3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包。

3.DiskLruCache是非Google官方编写,但获得官方认证的硬盘缓存类,该类没有限定在Android内,所以理论上java应用也可以使用DiskLreCache来缓存。该类你可以在这个下载:

方法与流程

1.要想实现图片三级缓存,就需要将图片下载到本地,我们所有网络图片请求都是通过Volley来统一管理的,Volley需要我们声明一个RequesQueuetManager来维持请求队列,因此,我们首先定义RequesQueuetManager类来管理RequesQueuetManager,代码如下:


1. publicclassRequesQueuetManager{
2. publicstaticRequestQueue mRequestQueue =Volley.newRequestQueue(DemoApplication.getInstance());
3. 
4. publicstaticvoid addRequest(Request<?> request,Objectobject){
5. if(object!=null){
6.  request.setTag(object);
7. }
8.  mRequestQueue.add(request);
9. }
10. 
11. publicstaticvoid cancelAll(Object tag){
12.  mRequestQueue.cancelAll(tag);
13. }
14. }

因为RequestQueue需要一个Context类型参数,所以我们在Volley.newRequestQueue(DemoApplication.getInstance())这句里传入了DemoApplication.getInstance(),这个静态方法是什么呢?就是应用的application实例,我们自定义一个application,然后将这个application传入给RequestQueue,我们自定义的application如下:

1. publicclassDemoApplicationextendsApplication{
2. publicstaticString TAG;
3. privatestaticDemoApplication application;
4. publicstaticDemoApplication getInstance(){
5. return application;
6. }
7. 
8. @Override
9. publicvoid onCreate(){
10. super.onCreate();
11.  TAG =this.getClass().getSimpleName();
12.  application =this;
13. }
14. }

要记得将自定义的application添加到AndroidManifest.xml文件的application标签的name属性里:

2.RequestQueue是负责图片请求顺序的,具体的图片请求工作由Volley里的ImageLoader类来完成,new它的时候它会接收两个参数,一个是我们刚刚声明的RequestQueue请求队列,另外一个是ImageLoader.ImageCache接口,看名字就知道,这个接口是方便我们写缓存用的,也就是说,我们接下来的二级缓存与三级缓存就是在实现此基础上进行的:首先我们新建ImageLreCache类来让它继承LreCache并实现ImageLoader.ImageCache接口,秉着父债子偿的原理,父类没有实现的接口子类就需要来实现,所以我们需要重写LreCache类的sizeOf方法,须要重写ImageLoader.ImageCache的getBitmapputBitmap方法。

于是该类下会有如下三个函数:


1. @Override
2. protectedint sizeOf(String key,Bitmap bitmap){
3. if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.HONEYCOMB_MR1){
4. return bitmap.getByteCount();
5. }
6. // Pre HC-MR1
7. return bitmap.getRowBytes()* bitmap.getHeight();
8. }
9. 
10. 
11. 
12. @Override
13. publicBitmap getBitmap(String s){
14. returnget(s);
15. }
16. 
17. @Override
18. publicvoid putBitmap(String s,Bitmap bitmap){
19.  put(s,bitmap)
20. }

sizeOf方法是LruCache 留给子类重写来衡量Bitmap大小的函数,因为LruCache里面会对size大小是否小于0进行判断,size小与0的Bitmap会抛出IllegalStateException异常。

getBitmap与putBitmap方法是ImageLoader.ImageCache留给子类实现的接口,Volley在请求网络数据时会先回调getBitmap方法来查看缓存中是否有所需图片,有的话会直接使用缓存的图片,没有再去请求,同理,当Volley下载完图片后会来回调putBitmap方法来将图片进行缓存。所以,我们实现这两个方法,然后在方法里直接使用LreCache的get与set方法即可。

综上,我们就将二级缓存完成,接下来一鼓作气,在此基础上完成硬盘级的第三级缓存,DiskLruCache的使用方法会稍微有些复杂,首先,我们需要DiskLruCache实例,它的构造方法是私有的,所以我们需要通过它提供的open方法来生成。open原型如下:


1. /**
2.  * Opens the cache in {@code directory}, creating a cache if none exists
3.  * there.
4.  *
5.  * @param directory a writable directory
6.  * @param appVersion
7.  * @param valueCount the number of values per cache entry. Must be positive.
8.  * @param maxSize the maximum number of bytes this cache should use to store
9.  * @throws java.io.IOException if reading or writing the cache directory fails
10.  */
11. publicstaticDiskLruCache open(File directory,int appVersion,int valueCount,long maxSize)
12. throwsIOException{}

它会接收四个参数,directory是缓存路径对应的File类,appVersion代表缓存版本,valueCount代表每个key对应的缓存个数,一般为1,maxSize代表缓存文件最大size。所以,在Android里,directory与appVersion可以从系统中获得,因此我们会这样写:


1. privatestaticDiskLruCache mDiskLruCache =DiskLruCache.open(getDiskCacheDir(DemoApplication.getInstance(),CACHE_FOLDER_NAME),
2.  getAppVersion(DemoApplication.getInstance()),1,10*1024*1024);
3. 
4. 
5. //该方法会判断当前sd卡是否存在,然后选择缓存地址
6. publicstaticFile getDiskCacheDir(Context context,String uniqueName){
7. String cachePath;
8. if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
9. ||!Environment.isExternalStorageRemovable()){
10.  cachePath = context.getExternalCacheDir().getPath();
11. }else{
12.  cachePath = context.getCacheDir().getPath();
13. }
14. returnnewFile(cachePath +File.separator + uniqueName);
15. }
16. 
17. //获得应用version号码
18. publicint getAppVersion(Context context){
19. try{
20. PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(),0);
21. return info.versionCode;
22. }catch(NameNotFoundException e){
23.  e.printStackTrace();
24. }
25. return1;
26. }

在获得DiskLruCache实例后,我们就可以来完善 ImageLoader.ImageCache接口下的getBitmap与putBitmap方法。主要思想是在方法里多一层逻辑判断,当图片不在LruCache时,再次查询DiskLruCache中是否存在,存在的话去取出然后转换成Bitmap并返回。需要注意的是DiskLruCache关于缓存内容的读取与写入是通过其内部封装的Editor与Snapshot两给类来实现,所以代码会稍有些复杂,但是很好理解。完善后的代码如下:


1. @Override
2. publicBitmap getBitmap(String s){
3. String key = hashKeyForDisk(s);
4. try{
5. if(mDiskLruCache.get(key)==null){
6. returnget(s);
7. }else{
8. DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
9. Bitmap bitmap =null;
10. if(snapShot !=null){
11. InputStreamis= snapShot.getInputStream(0);
12.  bitmap =BitmapFactory.decodeStream(is);
13. }
14. return bitmap;
15. }
16. }catch(IOException e){
17.  e.printStackTrace();
18. }
19. returnnull;
20. }
21. 
22. @Override
23. publicvoid putBitmap(String s,Bitmap bitmap){
24.  put(s,bitmap);
25. String key = hashKeyForDisk(s);
26. try{
27. if(null== mDiskLruCache.get(key)){
28. DiskLruCache.Editor editor = mDiskLruCache.edit(key);
29. if(editor !=null){
30. OutputStream outputStream = editor.newOutputStream(0);
31. if(bitmap.compress(CompressFormat.JPEG,100, outputStream)){
32.  editor.commit();
33. }else{
34.  editor.abort();
35. }
36. }
37.  mDiskLruCache.flush();
38. }
39. }catch(IOException e){
40.  e.printStackTrace();
41. }
42. 
43. }

这样,缓存类以完结,然我们回到这一步的最开始,有了这个实现ImageLoader.ImageCache接口的类就可以用来生成ImageLoader实例了:


1. publicstaticImageLoader mImageLoder =newImageLoader(RequesQueuetManager.mRequestQueue,newImageLreCache());

3.拿到mImageLoder之后我们就可以请求网络上的图片了,请求的函数为get,接收的参数为图片远程地址url,回调接口listener,图片需要的宽度maxWidth与高度maxHeight,这里比较难以理解的是listener参数,它是ImageLoader的内部接口ImageListener,主要是图片请求成功或者失败的两个回调方法,这里我们写一个统一生成isterner的函数:


1. publicstaticImageLoader.ImageListener getImageLinseter(finalImageView view,
2. finalBitmap defaultImageBitmap,finalBitmap errorImageBitmap){
3. 
4. returnnewImageLoader.ImageListener(){
5. @Override
6. publicvoid onResponse(ImageLoader.ImageContainer imageContainer,boolean b){
7. if(imageContainer.getBitmap()!=null){
8.  view.setImageBitmap(imageContainer.getBitmap());
9. }elseif(defaultImageBitmap !=null){
10.  view.setImageBitmap(defaultImageBitmap);
11. }
12. }
13. 
14. @Override
15. publicvoid one rrorResponse(VolleyError volleyError){
16. if(errorImageBitmap !=null){
17.  view.setImageBitmap(errorImageBitmap);
18. }
19. }
20. };
21. }

这个函数接收view,defaultImageBitmap,errorImageBitmap三个参数,当图片请求成功后将下载后的bitmap显示到view上,失败则显示errorImageBitmap。

综上,我们可以封装一个函数来提供给外部,接收6个参数,实现”one line”式编程,让网络图片请求变的更容易。代码如下:


1. /**
2.  * 外部调用次方法即可完成将url处图片现在view上,并自动实现内存和硬盘双缓存。
3.  * @param url 远程url地址
4.  * @param view 待现实图片的view
5.  * @param defaultImageBitmap 默认显示的图片
6.  * @param errorImageBitmap 网络出错时显示的图片
7.  * @param maxWidtn
8.  * @param maxHeight 
9.  */
10. publicstaticImageLoader.ImageContainer loadImage(finalString url,finalImageView view,
11. finalBitmap defaultImageBitmap,finalBitmap errorImageBitmap,int maxWidtn,int maxHeight){
12. return mImageLoder.get(url, getImageLinseter(view,defaultImageBitmap,
13.  errorImageBitmap),maxWidtn,maxHeight);
14. }

效果

我们先看一下它的使用方法,首先拿到控件:


1. ImageView iv =(ImageView) findViewById(R.id.iv_hello);

接着调用如下方法即可将图片http://ww2.sinaimg.cn/large/7cc829d3gw1eahy2zrjlxj20kd0a10t9.jpg显示在上面的控件上:


1. ImageCacheManger.loadImage("http://ww2.sinaimg.cn/large/7cc829d3gw1eahy2zrjlxj20kd0a10t9.jpg", iv,
2.  getBitmapFromResources(this, R.drawable.ic_launcher), getBitmapFromResources(this, R.drawable.error));

所谓无图无真相,这是请求成功的效果(在请求成功后,断网后第二次打开依然可以显示,因为缓存在本地硬盘里):

DiskLruCache缓存在硬盘中的文件:

代码

整个项目你可以在这里Clone或者下载:https://github.com/saymagic/PictureThreeCache

标签:缓存,bitmap,ImageLoader,Android,图片,DiskLruCache,三级,view
From: https://blog.51cto.com/u_16112859/6411003

相关文章

  • Android NDK链接静态库动态库
    在NDK中使用LOCAL_LDLIBS进行链接LOCAL_LDLIBS:=/home/tsh/work/ndk-demo/dobby/libdobby.aLOCAL_LDLIBS+=-llog如果在AOSP环境中中可以使用LOCAL_SHARED_LIBRARIES:=liblogLOCAL_STATIC_LIBRARIES+=/home/tsh/work/ndk-demo/dobby/libdobby.a......
  • Kali Linux中使用Vysor对Android设备进行投屏
    在Window有很多Android投屏软件,Linux相对较少,在网上查找大部分的人都是使用的scrcpy这个工具,由于在最新版中Kali无法自带的apt仓库下载该软件┌──(junglezt㉿Ubuntu)-[~]└─$sudoaptinstallscrcpy正在读取软件包列表...完成正在分析软件包的依赖关系树...完成正在读......
  • oscache缓存技术
    oscache缓存技术:[url]http://j2eemylove.iteye.com/blog/939828[/url][b]1、OSCache是什么?[/b]OSCache标记库由OpenSymphony设计,它是一种开创性的缓存方案,它提供了在现有JSP页面之内实现内存缓存的功能。OSCache是个一个被广泛采用的高性能的J2EE缓存框架,OSCache还能应用于......
  • 基于Android 网上商城系统设计与实现
    随着移动通信与Internet的飞速发展及相互融合,GPRS使无线网络高速接入到Internet成为现实,移动用户从而可以享受到Internet提供的服务。这样,移动终端不再仅是通讯网络的终端,还将成为互联网的终端。本文首先给出了系统研究背景,对当前手机操作系统发展做了简单的介绍。Android是基于Li......
  • 写给Android工程师的协程指南
    这是一份写给Android工程师的协程指南,希望在平静的2023,给大家带来一些本质或者别样的理解。引言在Android的开发世界中,关于异步任务的处理一直不是件简单事。面对复杂的业务逻辑,比如多次的异步操作,我们常常会经历回调嵌套的情况,对于开发者而言,无疑苦不堪言。当Kotlin协程出......
  • 踩坑|以为是Redis缓存没想到却是Spring事务!
    前言  最近碰到了一个Bug,折腾了我好几天。并且这个Bug不是必现的,出现的概率比较低。一开始我以为是旧数据的问题,就让测试重新生成了一下数据,重新测试。由于后面几轮测试均未出现,我也就没太在意。  可惜好景不长,测试反馈上次的问题又出现了。于是我立马着手排查,根据日志的表......
  • LRU缓存与LinkedHashMap源码
    今天再刷LeetCode时,遇到了第146题LRU缓存。题目如下:请你设计并实现一个满足LRU(最近最少使用)缓存约束的数据结构。实现LRUCache类:LRUCache(intcapacity)以正整数作为容量capacity初始化LRU缓存intget(intkey)如果关键字key存在于缓存中,则返回关键字的值,否......
  • HTTP的缓存机制是什么?
    HTTP缓存机制是一种在Web开发中常用的技术,它旨在提高性能和减少网络流量。通过缓存,可以避免不必要的网络请求,减少服务器负载,并加快页面加载速度。下面是关于HTTP缓存机制的详细介绍。HTTP缓存机制的基本原理是将Web资源(如HTML、CSS、JavaScript、图像等)保存在客户端或中间......
  • Android-滑块验证View
    一个简单的滑块验证view。项目地址:https://gitee.com/819158327/sliderview。 ......
  • 大白话讲解数据库的三级模式(所谓的内外模式在生活中到底是什么东西?)
    具象化理解数据库的三级模式形象一点来说,把数据看做货物,数据库是仓库,模式就是表格。你有一个仓库,仓库里成千上万的货物,随便你怎么堆,你堆个正方体,堆个圆柱体,甚至随便乱堆都行,你怎么堆的叫内模式。完事你写了一张表,表上对全部货物按某个标准分类,而且标清了啥货物在哪(这个是模式内......