Android笔记---缓存问题

Bitmap Cache
关于Bitmap可以看下:
https://blog.csdn.net/weixin_43927892/article/details/106209563
常用的缓存策略是LrnCache和DiskLruCache,其中LruCache常被用作内存缓存,而DiskLruCache常被用做存储缓存。Lru是Least Recently User的缩写,即最少使用的算法,当缓存快慢时,会淘汰近期最少使用的缓存目标。

Bitmap:在安卓中指的是一张图片可以是png格式也可以是jpg等其他常见的图片格式。如何加载一张图片?BitmapFactory类提供了四类方法:decodeFile、decodeResource、decodeStream和decodeByteArray,分别用于支持从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象,其中decodeFile和decodeResource又间接调用了decodeStream方法,这四类方法最终是在Android的底层实现的,对应着BitmapFacory类的几个native方法。

如何高效的加载Bitmap呢?其实核心思想也很简单,那就是采用BitmapFactory.Options来加载所需尺寸的图片。这里假设通过ImageView来显示图片,很多时候ImageView并没有图片的原始尺寸那么大,这个时候把整个图片加载进来后在设给ImageView并没有图片的原始尺寸那么大,这个时候把整个图片加载进来后在设给ImageView,这显然是没有必要的,因为ImageView并没有办法显示原始的图片。通过BitmapFactory.Options就可以按一定的采样率来加载缩小后的图片,将缩小后的图片在ImageView中显示,这就会降低内存占用从而在一定程度上避免OOM,提高了Bitmap加载时的性能。BitmapFactory提供的加载图片的四类方法都支持BitmapFactory.Options参数,通过他们就可以很方面对一个图片进行采样缩放。

通过BitmapFactory.Options来缩放图片,主要用到了它的inSampleSize参数,即采样率。当inSampleSize为1时,采样后的图片大小为图片的原始大小;当inSampleSize大于1时,比如为2,那么采样后的图片其宽高均为原图大小的1/2,而像素为原图的1/4其占内存的大小也为原图的1/4。拿一张假设采用ARGB8888格式存储,那么它占有的内存为1024×1024×4,即4MB,如果inSampleSize为2,那么采样后的图片其占内存只有512×512×4,即1MB。可以发现采样率为4则缩放比列就是(2*4=16)1/16,如果小于1其作用相当于1.

如果ImageView的大小是100×100像素,而图片的原始大小为200×200,那么只需将采样率设置为2即可,但是如果图片大小为200×300呢这时候采样率还应该是2,向下取整。

通过采样率可以有效的加载图片,那么如何有效获取采样率吗?获取采样率的流程:

​ (1)将BitmapFactory.Options的inJustDecodeBounds参数设置为true并加载图片。

​ (2)从BitmapFactory.Options中取出图片的原始高宽信息,他们对应于outWidth和outHeight参数。

(3)根据采样率的规格并结合目标View的所需大小计算出采样率inSampleSize。

(4)将BitmapFactory.Options的inJustDecodeBounds参数设为false(设为true时Bitmap只会解析图片的原始高宽信息)

在这里插入图片描述
在这里插入图片描述
有了上面的两个方法,实际使用的时候就很简单了,比如ImageView所期望的图片大小为100×100像素,这个时候就可以通过如下方式高效地加载并显示图片:
在这里插入图片描述

安卓中的缓存策略

缓存:当程序第一次从网络加载图片后,就将其缓存到存储设备上,有时候为了提高应用的用户体验,往往还会把图片在内存中在缓存一份,这样当应用打算从网络上请求一张图片时,程序会首先从内存中去获取,再从存储设备中获取,如果存储设备中也没与就从网络上下载这张图片,从内存中加载比从设备中加载图片还块。
缓存策略主要包含缓存的添加、获取和删除这三个操作。
采用LRU算法的缓存有两种:LruCache和DiskLruCache,LruCache用于实现内存缓存,而DiskLruCache则充当了存储设备(磁盘)缓存,通过这两者完美结合,就可以很方面的实现一个很高使用价值的ImageLoader。

一个优秀的ImageLoader应该具备如下功能:
图片的同步加载
图片的异步加载
图片的压缩
内存缓存
磁盘缓存
网络拉取

图片的同步加载是指能够以同步的方式向调用者提供所加载的图片,这个图片可能是从内存缓存中读取的也可能是从磁盘缓存中读取的。图片的异步加载是一个很有用的功能,很多时候调用者不想再单独的线程中以同步的方式获取图片,这个时候ImageLoader内部需要自己再线程中加载图片并将图片设置给所需的ImageView。
图片压缩OOM概率的有效手段,ImageLoader必须合适地处理图片的压缩问题,单独设计了一个类ImageResizer实现如下。
在这里插入图片描述
在这里插入图片描述

内存缓存和磁盘缓存是ImageLoader的核心,也是ImageLoader的意义之所在,通过这两级缓存极大的提高了程序的效率并且有效地降低了对用户造成的流量消耗,只有当两级缓存都不可用时才需要从网络中拉取图片。
除此之外,ImageLoader还需要处理一些特殊的情况,比如在ListView或者GridView中,View复用即是他们的优点也是他们的缺点,考虑一种情况,再ListView或者GridView中,假设一个ItemA正在从网络上加载图片,它对应的ImageView为A,这个时候用户快速向下滑动列表,很可能itemB复用了很可能ItemB复用了ImageViewA,然后等了一会之前的图片下载完毕了。如果直接给ImageViewA设置图片,由于这个时候ImageViewA被B所复用,但是itemB显示的图片显然不是itemA刚下载好的图片,这个时候就会出现itemB中显示itemA的图片,这就是常见的列表错位位置,ImageLoader需要正确的处理这些情况直接采用主线程的Looper来构造Handler对象,这就使得ImageLoader可以再主线程构造了。另外为了解决由于View复用所导致的列表错位这一问题,在给ImageView设置图片之前都会检查它的url有没有变化,如果发生改变就不再给它设置图片,这样就解决了列表错位的问题。

ImageLoader使用的照片墙效果
实现照片墙需要用到GridView,需要先准备好GridView所需的布局文件以及item的布局文件。
在这里插入图片描述
GridView的item的布局文件中并没有采用ImageView,而是采用自定义的正方形控件,要实现一个宽高相等的ImageView是非常简单的一件事,只需要在它的onMeasure方法中稍微处理。我们在SquareImageView的onMeasure方法中很巧妙地将heightMeasureSpec替换为widthMeasureSpec,这样什么都不用做就可以做一个宽高相等的ImageView了。
在这里插入图片描述
接着需要实现一个BaseAdapter给GridView使用,下面的代码展示了ImageAdapter的实现细节其中mUrList中存储的是图片的url:
在这里插入图片描述
在这里插入图片描述
ImageAdapter的实现过程非常简洁,getView方法中核心代码只有一句话,那就是:mImageLoader.bindBitmap(uri,image View,mImageWidth,mImageWidth)。通过bindBitmap方法很轻松的将复杂的图片加载过程交给了ImageLoader,ImageLoader加载图片以后会把图片自动设置给imageView。
接着将ImageAdapter设置给GridView,如下所示。
在这里插入图片描述
本节中的照片墙应用首次运行会从网络上加载大量图片,在非WiFi环境下,打开应用时会弹出如下提示,请读者运行时注意一下。
在这里插入图片描述

优化列表的卡顿现象
这个问题困扰了很多开发者,其实答案很简单,不要再主线程中做太耗时的操作即可提高滑动的流畅度,可以从三个方面来说明这个问题。
第一:不要再getView中执行耗时操作。对于上面的列子来说,如果直接在getView方法中加载图片,肯定会导致卡顿,因为加载图片是一个耗时操作,这种操作必须通过异步的方式来处理,就像ImageLoader实现的那样。

其次,控制异步的执行频率。这一点也很重要,对于列表来说,仅仅在getView中采用异步操作时不够的。考虑一种情况,以照片墙来说,再getView方法中会通过ImageLoader的bindBitmap方法来异步加载图片,但是如果刻意上线滑动,这就会在一瞬间产生上百个异步任务,这些异步任务会造成线程池的拥堵并随着即大量的UI更新操作,这是没有意义的。由于一瞬间存在大量的UI更新操作,这些UI操作是运行在主线程的,这就会造成一定程度的卡顿。如何解决这个问题呢?可以考虑在列表滑动的时候停止加载图片,尽管这个过程是异步的,等列表停下来以后再加载图片仍可以获得良好的用户体验。具体实现时,可以给ListView或者GridView设置setOnScrollListener,并在OnScrollListtener的onScrollStateChanged方法中判断列表是否处于滑动状态如果停止的话就加载图片。
在这里插入图片描述
然后再getView方法中,仅当列表静止时才能加载图片。
在这里插入图片描述
第三开启硬件加速:通过设置:android:hardwareAccelerated=“true”
即可为活动加速,可以解决一些莫名卡顿问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值