一、磁盤緩存
其實磁盤緩存的原理沒什麼好說的?爲什麼這麼說呢?因爲Glide磁盤緩存的實現使用的也是JakeWharton大神的DisLruCache,所以呢,我們也是直接拿過來用即可,直接把這三個文件直接Copy即可。下面我們來說一下磁盤緩存怎麼去使用:
首先我們先寫一個磁盤緩存用到的接口類DiskCache
public interface DiskCache {
interface Writer {
boolean write(File file);
}
File get(Key key);
void put(Key key, Writer writer);
void delete(Key key);
void clear();
}
get,put,delete,clear這四個方法看名字就知道具體的作用,至於Writer和它的write方法,主要就是對圖片進行解碼,用到的時候我們具體說。下面是DiskCache的實現類DiskLruCacheWrapper:
public class DiskLruCacheWrapper implements DiskCache {
final static int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
final static String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";
private MessageDigest MD;
private DiskLruCache diskLruCache;
public DiskLruCacheWrapper(Context context) {
this(new File(context.getCacheDir(), DEFAULT_DISK_CACHE_DIR), DEFAULT_DISK_CACHE_SIZE);
}
protected DiskLruCacheWrapper(File directory, long maxSize) {
try {
MD = MessageDigest.getInstance("SHA-256");
//打開一個緩存目錄,如果沒有則首先創建它,
// directory:指定數據緩存地址
// appVersion:APP版本號,當版本號改變時,緩存數據會被清除
// valueCount:同一個key可以對應多少文件
// maxSize:最大可以緩存的數據量
diskLruCache = DiskLruCache.open(directory, 1, 1, maxSize);
} catch (Exception e) {
e.printStackTrace();
}
}
public String getKey(Key key) {
key.updateDiskCacheKey(MD);
return new String(Utils.sha256BytesToHex(MD.digest()));
}
@Nullable
@Override
public File get(Key key) {
String k = getKey(key);
File result = null;
try {
DiskLruCache.Value value = diskLruCache.get(k);
if (value != null) {
result = value.getFile(0);
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 加入磁盤緩存
* @param key
* @param writer
*/
@Override
public void put(Key key, Writer writer) {
String k = getKey(key);
try {
DiskLruCache.Value current = diskLruCache.get(k);
if (current != null) {
return;
}
DiskLruCache.Editor editor = diskLruCache.edit(k);
try {
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void delete(Key key) {
String k = getKey(key);
try {
diskLruCache.remove(k);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void clear() {
try {
diskLruCache.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
}
簡單的說一下:
構造方法中主要做了一些初始化的工作,包括:計算Hash的算法名稱(我這裏用的是SHA-256),初始化磁盤緩存的位置,版本,大小等。其他的四個方法就是一些具體的操作,可以自己看看,都不難,如果有問題可以給我留言。
二、加載器
我們先看一張架構圖,然後我們再去理解這個加載器的流程
上面的架構圖並不是真正Glide的架構模式,這個是我們要寫的閹割版Glide的架構,不過流程基本沒變。
這裏的處理數據的模型只有4個,但是真正的Glide的數據處理模型有好幾十個,我這裏只拿出來幾個比較常用的來說。
我們一步一步來:
第一步:Glide在初始化的時候,都會把相應的數據處理模型註冊到ModelLoaderRegister中,模型中的Model指的是圖片資源的類型,Data指的是加載文件後的輸出類型,我這裏一律以InputStream的格式進行輸出。Factory主要就是創建相應ModelLoader來進行數據的加載(真正進行數據加載的其實是LoadData的fetcher,這個我們稍後說)。然後將這三個元素封裝在一個Entry類中,加入到ModelLoaderRegister中。
第二步:當圖片資源加入到Glide時,系統會根據資源的類型,在ModelLoaderRegister中查找,哪種數據模型可以處理這個圖片資源。當然查找的結果可能是多種,也可能是一種,所以我們使用List來裝。找到符合的數據模型後,Factory就會調用build來創建一個ModelLoader對象。比如圖片資源File類型時,找到的Factory是FileLoader.Factory,但它在build的時候,其實是ModelLoaderRegister根據Model爲Uri,Data爲InputStream進行build相應的ModelLoader(估計到這裏,好多同學就懵了。怎麼又回去了?FileLoader感覺什麼也沒做呢?是的,其實FileLoader更像是中轉站)。ModelLoaderRegister在build的時候,又會出現上面的問題,ModelLoader可能有一個,也有可能是多個,怎麼辦?我們還要保證返回值是同一類型。有的同學可能會說,可以使用List。我認爲這樣設計沒有問題。ModelLoader中都有一個buildData,方法的參數就是圖片資源,我們可以在這裏對List進行遍歷,拿出每個ModelLoader來進行handle,看看能不能處理圖片資源。但是這就要每個ModelLoader都要在這個方法中寫循環的重複代碼,很冗餘。所以Glide就創建了一個叫做MultiModelLoader的類,它也是ModelLoader,在這個類裏面有一個ModelLoader的集合。所以ModelLoaderRegister在build的時候,如果是一個則返回相應的ModelLoader;如果是多個,則統一放在MultiModelLoader的集合裏,判斷邏輯統一在MultiModelLoader的buildData方法來做。
第三步:如果圖片是網絡圖片,那麼ModelLoader最後會被定位爲HttpUriLoader,通過buildData,找對應的HttpUriFetcher進行圖片的獲取;如果圖片是本地圖片,那麼ModelLoader最後會被定位爲FileUriLoader,通過buildData,找到對應的Fetcher爲FileUriFetcher,然後進行資源的獲取。
加載器這部分很重要,但是的確很難理解,我們需要反覆的去看源碼纔可以,不是我幾句話就能說得清楚的,我這裏僅僅是拋磚引玉吧。代碼我就不貼了,類實在是不少,我會把這部分的代碼提供出來供大家研究。代碼
歡迎大家提問和糾錯!