提及缓存,你们可能很容易想到Http的缓存机制,LruCache,其实缓存最初是针对于网络而言的,也是狭义上的缓存,广义的缓存是指对数据的复用,我这里提到的也是广义的缓存,比较常见的是内存缓存以及磁盘缓存,不过要想进一步理解缓存体系,其实还须要复习一点计算机知识。java
CPU分为运算器跟控制器,是计算机的主要设备之一,功能主要是解释计算机指令以及处理计算机软件中的数据。计算机的可编程性主要是指对中央处理器的编程。中央处理器、内部存储器和输入/输出设备是现代电脑的三大核心部件。编程
存储器的种类不少,按用途能够分为主存储器和辅助存储器,下面依次介绍一下。缓存
又称内存是CPU能直接寻址的存储空间,它的特色是存取速率快。内存通常采用半导体存储单元,包括随机存储器(Random Access Memory)、只读存储器(Read Only Memory)和高级缓存(Cache)。服务器
辅助存储器又称外存储器,简称外存,对于电脑而言,一般说的是硬盘或者光盘等,对于手机通常指的是SD卡,不过如今不少厂商都已经整合在一块儿了网络
就是缓存的大小,到达这个限度以后,那么就须要进行缓存清理了框架
不论是内存缓存仍是磁盘缓存,缓存的容量都是有限制的,因此跟线程池满了以后的线程处理策略相似,缓存满了的时候,咱们也须要有相应的处理策略,常见的策略有:less
FIFO(first in first out):先进先出策略,相似队列。dom
LFU(less frequently used):最少使用策略,RecyclerView的缓存采用了此策略。ide
LRU(least recently used):最近最少使用策略,Picasso在进行内存缓存的时候采用了此策略。this
当缓存容量达到设定的容量的时候,会根据制定的策略进行删除相应的元素。
这个主要发生在内存缓存中,当生命周期段的对象持有了生命周期长的对象的引用就会发生内存泄露,解决这种问题一般有两种方式
实际上在防止内存泄露的过程当中这两种方式都使用地比较平凡,不过咱们大多数时候使用的仍是弱引用。
其实Java有四种引用,强引用,软引用,弱引用,虚引用,这些并没什么好说的,咱们平时使用最多的仍是弱引用,也就是WeakReference。
弱引用VS软引用:
只具备弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。
下面简单描述一下这两种防止内存泄露的方法的区别
RecyclerView的内部类LayoutManager持有了RecyclerView的使用,没有采用弱引用,可是提供了置空的方法
public static abstract class LayoutManager {
ChildHelper mChildHelper;
RecyclerView mRecyclerView;
@Nullable
SmoothScroller mSmoothScroller;
private boolean mRequestedSimpleAnimations = false;
boolean mIsAttachedToWindow = false;
private boolean mAutoMeasure = false;
private boolean mMeasurementCacheEnabled = true;
private int mWidthMode, mHeightMode;
private int mWidth, mHeight;
void setRecyclerView(RecyclerView recyclerView) {
if (recyclerView == null) {
//回收
mRecyclerView = null;
mChildHelper = null;
mWidth = 0;
mHeight = 0;
} else {
//初始化
mRecyclerView = recyclerView;
mChildHelper = recyclerView.mChildHelper;
mWidth = recyclerView.getWidth();
mHeight = recyclerView.getHeight();
}
mWidthMode = MeasureSpec.EXACTLY;
mHeightMode = MeasureSpec.EXACTLY;
}
复制代码
用Picasso中的Action为例,父类采用了WeakReference
Action父类
abstract class Action<T> {
final WeakReference<T> target;
Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
this.picasso = picasso;
this.request = request;
this.target =target ;
this.memoryPolicy = memoryPolicy;
this.networkPolicy = networkPolicy;
this.noFade = noFade;
this.errorResId = errorResId;
this.errorDrawable = errorDrawable;
this.key = key;
this.tag = (tag != null ? tag : this);
}
复制代码
ImageAction子类
class ImageViewAction extends Action<ImageView> {
Callback callback;
ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
Callback callback, boolean noFade) {
super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,tag, noFade);
this.callback = callback;
}
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
if (result == null) {
throw new AssertionError(
String.format("Attempted to complete action with no result!\n%s", this));
}
ImageView target = this.target.get();
if (target == null) {
return;
}
Context context = picasso.context;
boolean indicatorsEnabled = picasso.indicatorsEnabled;
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
}
复制代码
因为ImageView持有Context的引用,因此致使Activity回收以后,若是ImageView是强引用,那么GC就不会去回收,而采用了弱引用以后,一旦Activity被回收,那么ImageViewAction的引用不会干扰到Activity的回收。
根据业务须要能够自行设定,可是注意,缓存的其实判断时间都应该以服务器时间为准,能够从服务器的返回数据的Response的header中的时间戳做为判断依据。
内存缓存读取速度远远高于磁盘缓存,咱们都知道Picasso是采用了内存缓存跟磁盘缓存这两种缓存的,可是他获取的时候首先是从内存中进行读取,而后把磁盘缓存加到网络缓存中去,其实一开始,我不是这样子作的,我是把内存缓存,磁盘缓存以及网络缓存读取都实例化了一个Runnable,而后在加载下一页的时候,老是会出现图片闪烁,可是我用Picasso,UIL跟Glide就不会闪烁,可是当我设置Picasso他们的内存缓存策略为MemoryPolicy.NO_CACHE的时候,他们也会闪烁,下面展现一下闪烁的效果
其实上面两种状况都会出现闪烁,共同缘由就是由于内存缓存的问题,Picasso的issue里面有人提过,做者JakeWharton是这么回答的
是的200ms,若是Bitmap没有读取成功,那么就会出现闪烁,这样正好解释了上面的两种状况,因为咱们设置了占位图,第一种闪烁是由于咱们把内存缓存的读取放到了一个线程里面,线程的建立,切换这些都是须要时间的,那么就致使了总时间会超过200ms;同理,第二种状况若是没有设置内存缓存,那么只能从网络或磁盘中读取这个时间确定会超过200ms,一样会闪烁,因此这也是为何图片加载框架优先从内存中读取,当不设置内存缓存的时候也会闪烁的缘由。
同时磁盘缓存须要借助于Http缓存机制来保证缓存的时效性,后面会具体分析。
其实缓存的改变比较好理解,就是在使用内存缓存的时候须要注意防止内存泄露,使用磁盘缓存的时候须要注意结合Http的缓存机制来来确保缓存的时效性