寫SDK的時候,碰到一個小的需求,銀行的小圖標需要訪問服務器下載,並且要緩存在本地。
因爲SDK項目中銀行圖標類似圖片並不是很多,如果使用第三方框架的話太大,大材小用,而且類似加載圖片的第三方很多,如果SDK接入了volley,但是接入我們SDK的app卻使用了picasso,會導致同樣功能代碼的重複,方法數也容易超標。
所以就要自己寫一個小的框架,簡要描述就是給出圖片url,第一次進行網絡訪問,並緩存在本地(包括內存和硬盤緩存)。後續都可以從本地讀取。(不處理服務器給出的頭部,例如有效期等等)。
代碼:
git clone https://github.com/LxxCaroline/MyApplication.git
該小框架是主要模仿volley而寫的,有類似的幾個class,包括RequestQueue, ImageRequest, NetworkDispatcher, CacheDispatcher, HttpEngine(類似HurlStack), ImageCache, NetworkImageView
ImageRequest主要是封裝了請求,包括url,listener,bitmap,isCanceled,tracer(Arraylist主要是記錄該request流程的走向)
RequestQueue是中央處理樞紐,ImageRequest交給RequestQueue來處理,由RequestQueue開啓網絡線程NetworkDispatcher和緩存線程CacheDispatcher來處理一些將要處理的ImageRequest。一般ImageRequest是先通過CacheDispatcher來獲取,如果獲取不到,則通過NetworkDispatcher來獲取,之後再寫入到Cache中。ImageCache主要是來讀寫Cache的,這裏用到了LruCache和DiskLruCache。
NetworkImageView是專門用來加載圖片的,HttpEngine就是真正訪問網絡的地方。
在RequestQueue中有幾個重要的變量:
private Map<String, Queue< ImageRequest >> mWaitingRequests;
private PriorityBlockingQueue<ImageRequest> mNetworkQueue;
private PriorityBlockingQueue<ImageRequest> mCacheQueue;
第一個變量是記錄當往RequestQueue中加入request的時候,先檢測是否有相同url的request,如果有,則將該request加入到相同url的queue中去,這樣當第一個相同url的request返回的時候,可以把結果分享給queue中的request。
第二個參數是維護將要被網絡訪問的request隊列。
第三個參數是維護將要訪問Cache的request隊列。
剛剛說到RequestQueue中會開啓網絡和cache線程,一般來說cache訪問會很快,所以只用開一個cache線程。network比較慢,所以如果你項目中圖片訪問量大的話,則需要多開幾個線程,如果圖片訪問量不大的話,開一至兩個線程就可以了。
一個cache線程的話,都從requestQueue的mCacheQueue中取,而多個network線程的話,都從requestQueue的mNetworkQueue中取(所有線程共享)。
priorityBlockingQueue是無界隊列,並且具有優先級,可以以一定的順序去讀取request,至於如何定優先級,用戶可以自己定義,例如先來後到,或給某些request一些高的優先級。
在NetworkImageView中,拿到bitmap後,需要給自己setImageBitmap,但是注意,需要在主線程中進行。
原來NetworkImageView的代碼:
private ImageRequest request = null;
private RequestQueue queue;
public void setRequestQueue(RequestQueue queue){
this.queue = queue;
}
public void setImageUrl(String url) {
if (request != null) {
request.setListener(null);
}
request = new ImageRequest(url);
request.setListener(this);
queue.addRequest(request);
}
@Override
public void onResponse(final Bitmap bitmap) {
Log.d("RequestQueue-View", "setBitmap " + (bitmap != null));
post(new Runnable() {
@Override
public void run() {
Log.d("RequestQueue-View", "setBitmap succ");
setImageBitmap(bitmap);
}
});
}
@Override
protected void onDetachedFromWindow() {
if (request != null) {
request.isCanceled = true;
request.setListener(null);
}
super.onDetachedFromWindow();
}
發現在onResponse函數中,只打印了第一個log,第二個卻沒打印(也就是說runnable中的內容都沒有執行),導致圖片未顯示。。。
後來查到post(runnable)在view沒有被attach到window之前,是沒有效果的,後來代碼改成如下:
public void setImageUrl(String url) {
if (request != null) {
request.setListener(null);
}
request = new ImageRequest(url);
request.setListener(this);
if (isAttachedToWindow()) {
queue.addRequest(request);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (request != null) {
queue.addRequest(request);
}
}
接下來說下DiskLruCache,這個是硬盤緩存技術,這個類的代碼需從網上下,並非google撰寫,但得到官方認可。具體說明可以看這篇文章:
Android DiskLruCache緩存完全解析