androidapp缓存机制会⾃动清除,深⼊理解Android缓存机制
(⼀)缓存简介
概述
说起缓存,⼤家可能很容易想到Http的缓存机制,LruCache,其实缓存最初是针对于⽹络⽽⾔的,也是狭义上的缓存,⼴义的缓存是指对数据的复⽤,我这⾥提到的也是⼴义的缓存,⽐较常见的是内存缓存以及磁盘缓存,不过要想进⼀步理解缓存体系,其实还需要复习⼀点计算机知识。
CPU
CPU分为运算器跟控制器,是计算机的主要设备之⼀,功能主要是解释计算机指令以及处理计算机软件中的数据。计算机的可编程性主要是指对中央处理器的编程。中央处理器、内部存储器和输⼊/输出设备是现代电脑的三⼤核⼼部件。
存储器
存储器的种类很多,按⽤途可以分为主存储器和辅助存储器,下⾯依次介绍⼀下。
主存储器
⼜称内存是CPU能直接寻址的存储空间,它的特点是存取速率快。内存⼀般采⽤半导体存储单元,包括随机存储器(Random Access Memory)、只读存储器(Read Only Memory)和⾼级缓存(Cache)。
RAM:随机存储器可以随机读写数据,但是电源关闭时存储的数据就会丢失;
ROM:只能读取,不能更改,即使机器断电,数据也不会丢失
Cache:它是介于CPU与内存之间,常⽤有⼀级缓存(L1)、⼆级缓存(L2)、三级缓存(L3)(⼀般存在于Intel系列)。它的读写速度⽐内存还快,当CPU在内存中读取或写⼊数据时,数据会被保存在⾼级缓冲存储器中,当下次访问该数据时,CPU直接读取⾼级缓冲存储器,⽽不是更慢的内存。
辅助存储器
辅助存储器⼜称外存储器,简称外存,对于电脑⽽⾔,通常说的是硬盘或者光盘等,对于⼿机⼀般指的是SD卡,不过现在很多⼚商都已经整合在⼀起了
缓存类型
内存缓存:这⾥的内存主要指的存储器缓存
磁盘缓存:这⾥主要指的是外部存储器,电脑指的是硬盘,⼿机的话指的就是SD卡
缓存容量
就是缓存的⼤⼩,到达这个限度之后,那么就需要进⾏缓存清理了
缓存策略
不管是内存缓存还是磁盘缓存,缓存的容量都是有限制的,所以跟线程池满了之后的线程处理策略类似,缓存满了的时候,我们也需要有相应的处理策略,常见的策略有:
FIFO(first in first out):先进先出策略,类似队列。
LFU(less frequently used):最少使⽤策略,RecyclerView的缓存采⽤了此策略。
LRU(least recently used):最近最少使⽤策略,Picasso在进⾏内存缓存的时候采⽤了此策略。
当缓存容量达到设定的容量的时候,会根据制定的策略进⾏删除相应的元素。
内存泄露
简述android概述
这个主要发⽣在内存缓存中,当⽣命周期段的对象持有了⽣命周期长的对象的引⽤就会发⽣内存泄露,解决这种问题通常有两种⽅式
引⽤置空:将缓存中引⽤的对象置空,然后GC就能够回收这些对象
采⽤弱引⽤:采⽤弱引⽤关联对象,这样就能够不⼲涉对象的⽣命周期,以便GC能够正常回收
实际上在防⽌内存泄露的过程中这两种⽅式都使⽤地⽐较平凡,不过我们⼤多数时候使⽤的还是弱引⽤。
其实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 = Width();
mHeight = Height();
}
mWidthMode = MeasureSpec.EXACTLY;
mHeightMode = MeasureSpec.EXACTLY;
}
采⽤弱引⽤
⽤Picasso中的Action为例,⽗类采⽤了WeakReference
Action⽗类
abstract class Action {
final WeakReference 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.target =target ;
thisworkPolicy = networkPolicy;
this.key = key;
this.tag = (tag != null ? tag : this);
}
ImageAction⼦类
class ImageViewAction extends Action {
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 = ();
if (target == null) {
return;
}
Context context = t;
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的时候,他们也会闪烁,下⾯展⽰⼀下闪烁的效果
flicker
其实上⾯两种情况都会出现闪烁,共同原因就是因为内存缓存的问题,Picasso的issue⾥⾯有⼈提过,作者JakeWharton是这么回答的
是的200ms,如果Bitmap没有读取成功,那么就会出现闪烁,这样正好解释了上⾯的两种情况,由于我们设置了占位图,第⼀种闪烁是因为我们把内存缓存的读取放到了⼀个线程⾥⾯,线程的创建,切换这些都是需要时间的,那么就导致了总时间会超过200ms;同理,第⼆种情况如果没有设置内存缓存,那么只能从⽹络或磁盘中读取这个时间肯定会超过200ms,同样会闪烁,所以这也是为什么图⽚加载框架优先从内存中读取,当不设置内存缓存的时候也会闪烁的原因。
同时磁盘缓存需要借助于Http缓存机制来保证缓存的时效性,后⾯会具体分析。
总结
其实缓存的改变⽐较好理解,就是在使⽤内存缓存的时候需要注意防⽌内存泄露,使⽤磁盘缓存的时候需要注意结合Http的缓存机制来来确保缓存的时效性
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论