提及緩存,你們可能很容易想到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的緩存機制來來確保緩存的時效性