`
shuai1234
  • 浏览: 978345 次
  • 性别: Icon_minigender_1
  • 来自: 山西
社区版块
存档分类
最新评论

Android网络图片三级缓存策略

 
阅读更多

 

在移动应用中,我们一般将网络图片分为三个级别,第一级别是网络层,即根据图片的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来缓存。该类你可以在这个下载:http://cdn.saymagic.cn/150131132428.java

方法与流程

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

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

因为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. @Override
  8. publicvoid onCreate(){
  9. super.onCreate();
  10. TAG =this.getClass().getSimpleName();
  11. application =this;
  12. }
  13. }

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

http://cdn.saymagic.cn/150131111011.09.02.png

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. @Override
  10. publicBitmap getBitmap(String s){
  11. returnget(s);
  12. }
  13. @Override
  14. publicvoid putBitmap(String s,Bitmap bitmap){
  15. put(s,bitmap)
  16. }

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

在获得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. @Override
  22. publicvoid putBitmap(String s,Bitmap bitmap){
  23. put(s,bitmap);
  24. String key = hashKeyForDisk(s);
  25. try{
  26. if(null== mDiskLruCache.get(key)){
  27. DiskLruCache.Editor editor = mDiskLruCache.edit(key);
  28. if(editor !=null){
  29. OutputStream outputStream = editor.newOutputStream(0);
  30. if(bitmap.compress(CompressFormat.JPEG,100, outputStream)){
  31. editor.commit();
  32. }else{
  33. editor.abort();
  34. }
  35. }
  36. mDiskLruCache.flush();
  37. }
  38. }catch(IOException e){
  39. e.printStackTrace();
  40. }
  41. }

这样,缓存类以完结,然我们回到这一步的最开始,有了这个实现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. returnnewImageLoader.ImageListener(){
  4. @Override
  5. publicvoid onResponse(ImageLoader.ImageContainer imageContainer,boolean b){
  6. if(imageContainer.getBitmap()!=null){
  7. view.setImageBitmap(imageContainer.getBitmap());
  8. }elseif(defaultImageBitmap !=null){
  9. view.setImageBitmap(defaultImageBitmap);
  10. }
  11. }
  12. @Override
  13. publicvoid onErrorResponse(VolleyError volleyError){
  14. if(errorImageBitmap !=null){
  15. view.setImageBitmap(errorImageBitmap);
  16. }
  17. }
  18. };
  19. }

这个函数接收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));

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

http://cdn.saymagic.cn/150131132130.png

DiskLruCache缓存在硬盘中的文件:

http://cdn.saymagic.cn/150131132219.png

代码

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

本文地址

分享到:
评论

相关推荐

    Android图片下载三级缓存策略源码Demo

    总的来说,"Android图片下载三级缓存策略源码Demo"提供了一个高效且稳定的图片加载解决方案,通过内存缓存、软引用和磁盘缓存的结合,实现了快速响应用户请求,降低了网络依赖,优化了应用性能。对于学习Android图片...

    Android图片下载三级缓存策略源码的Demo

    本文将深入探讨“Android图片下载三级缓存策略源码的Demo”,主要涉及LruCache、软引用(SoftReference)以及DiskLruCache这三种缓存技术。 首先,我们来理解一下LruCache。LruCache(Least Recently Used Cache)...

    android中图片的三级缓存cache策略(内存/文件/网络)

    Android中图片的三级缓存策略是提高应用性能、降低网络消耗的关键技术。通过合理地使用内存、文件和网络缓存,开发者可以为用户提供更快的图片加载速度,同时保持应用的稳定性。正确理解和实践这一策略,对于优化...

    Android 图片三级缓存的一个好用demo(ImageLoaderDemo)

    "Android 图片三级缓存的一个好用demo(ImageLoaderDemo)" 提供了一个有效的解决方案,旨在提高图片加载速度并降低对网络资源的依赖。这个Demo主要涉及了Android中的图片缓存策略,包括内存缓存、磁盘缓存以及网络...

    Android自定义图片三级缓存

    图片的三级缓存策略,即内存、硬盘和网络(其实网络不算是缓存,姑且算是吧~~),内存缓存使用的时LRUCache,这是一个存放键值对的列表,如果内存不够会根据最近使用次数的多少来删除其中的一个item,最少使用的就会...

    android图片三级缓存工具类

    综上所述,"android图片三级缓存工具类"是Android开发中提高图片加载效率的一个实用组件,它结合了内存、磁盘和网络三级缓存策略,以确保图片的快速加载和良好的用户体验。通过合理地使用和定制这个工具类,开发者...

    Android 图片三级缓存

    "Android 图片三级缓存"是一种优化策略,它通过结合内存缓存、硬盘缓存以及网络缓存来高效管理图片资源。这里我们将详细探讨这一主题,尤其是LruCache类在其中的作用。 首先,我们要理解什么是三级缓存。一级缓存...

    Android图片加载的三级缓存Demo

    为了优化用户体验,防止因内存消耗过大而导致的Out of Memory(OOM)异常,通常会采用三级缓存策略。这个"Android图片加载的三级缓存Demo"就是针对这一问题的一个实例,它通过LruCache、DiskLruCache这两种缓存机制...

    android ImageLoader图片三级缓存框架

    网络缓存是三级缓存的最后防线,虽然最慢,但能保证图片的最终获取。 ImageLoader的特性与优势: - **异步加载**:ImageLoader支持异步加载图片,这意味着在用户滚动列表时,图片会后台加载,不会阻塞主线程,提升...

    android 图片的二级缓存

    在Android应用开发中,图片加载是常见...正确地设计和使用二级缓存策略,可以显著提升用户体验,同时减少应用崩溃的风险。在实际开发中,应结合具体项目需求,选择合适的图片加载库,并优化缓存策略,以达到最佳性能。

    浅谈Android 中图片的三级缓存策略

    三级缓存策略,最实在的意义就是 减少不必要的流量消耗,增加加载速度 。 如今的 APP 网络交互似乎已经必不可少,通过网络获取图片再正常不过了。但是,每次启动应用都要从网络获取图片,或者是想重复浏览一些图片的...

    Android图片三级缓存策略(网络、本地、内存缓存)

    通常情况下,Android应用程序中图片的缓存策略采用“内存-本地-网络”三级缓存策略,首先应用程序访问网络拉取图片,分别将加载的图片保存在本地SD卡中和内存中,当程序再一次需要加载图片的时候,先判断内存中是否...

    android安卓图片的三级缓存DEMO 三层缓存示例下载 强引用 软引用

    在Android中,图片的三级缓存通常指的是内存缓存、磁盘缓存和网络缓存。这种缓存策略旨在提高图片加载速度,减少网络请求,同时节省系统资源。 1. **内存缓存(强引用)**:内存缓存使用HashMap等数据结构存储图片...

    Android图片的三级缓存

    总的来说,Android图片的三级缓存机制是优化图片加载性能的关键,它结合了速度、容量和网络效率,为用户提供流畅的体验。理解并掌握这一机制对于Android开发者来说至关重要,不仅能够提升应用性能,还能有效避免资源...

    ListView异步加载图片三级缓存

    总结起来,"ListView异步加载图片三级缓存"是一个重要的Android性能优化技巧,它结合了异步处理、内存管理、磁盘操作以及网络通信,为用户提供流畅、高效的图片浏览体验。在LazyLoaderDemo这样的示例代码中,我们...

    ImageLoaderDemo图片三级缓存

    "ImageLoaderDemo图片三级缓存"是一个典型的图片加载优化方案,它通过建立三级缓存机制来高效地管理和加载图片资源,减少网络请求,提升用户体验。 首先,我们来详细解析这个项目的三个缓存层级: 1. **内存缓存...

    图片三级缓存JAR包

    综上所述,"图片三级缓存JAR包"是解决Android应用中图片加载效率问题的重要工具,通过合理的缓存策略,实现了内存、磁盘和网络的协同工作,提升了用户体验。开发者在使用时,应理解其工作原理并结合具体项目进行适配...

    图片三级缓存,防止OOM

    "图片三级缓存"是一种优化策略,旨在高效地加载和存储图片,防止因内存溢出(Out Of Memory, OOM)问题导致应用崩溃。在这个场景中,我们主要关注的是使用Diskspace LRU Cache(磁盘空间最近最少使用缓存)实现图片...

    android 三级缓存

    这里的"android 三级缓存"指的是在Android应用中实现的一种高效的缓存机制,它包括内存缓存、磁盘缓存以及远程数据源三层,以此来减少对网络的依赖,提高数据读取速度。 首先,我们来详细解释这三级缓存: 1. 内存...

Global site tag (gtag.js) - Google Analytics