Android-Universal-Image-Loader是一個開源項目,負責處理圖片的載入和緩衝。閑暇之時看了一些原始碼,特記錄之。
說道圖片檔案(磁碟)緩衝,需要考慮如下的因素
1) 快取檔案的名稱的定義
2) 緩衝的大小
3) 快取檔案的方式:比如限時儲存檔案等,圖片壓縮後的格式,壓縮率等等。
該項目對磁碟緩衝儲存檔案時對檔案名稱的修改也做了支援:在快取檔案時對檔案名稱的修改提供了兩種方式,每一種方式對應了一個Java類
1) HashCodeFileNameGenerator,該類負責擷取檔案名稱的hashcode然後轉換成字串。
2) Md5FileNameGenerator,該類把源檔案的名稱同過md5加密後儲存。兩個類都繼承了FileNameGenerator介面
它們之間的關係如所示
<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHAgYWxpZ249"left">在DefaultConfigurationFactory類中提供了一個Factory 方法createFileNameGenerator,該方法返回了一個預設的FileNameGenerator對象:HashCodeFileNameGenerator.
public static FileNameGenerator createFileNameGenerator() {return new HashCodeFileNameGenerator();}
下面說說磁碟緩衝的具體實現:
首先定義了DiscCacheAware介面,該介面提供了如下方法
方法名 |
說明 |
返回值 |
getFileDectory() |
返回磁碟緩衝的根目錄 |
File |
get(String imageUri) |
根據uri從緩衝中擷取圖片 |
File |
save(imageUri,InputStream iamgeStream,IoUtils.CopyListener listener) |
把圖片儲存在磁碟緩衝上 |
boolean |
save(imageUri,Bitmap bitmap) |
儲存bitMap對象到磁碟緩衝上 |
Boolean |
remove(imageUri) |
根據imageUri刪除檔案 |
boolean |
close() |
關閉磁碟緩衝,釋放資源 |
void |
clear() |
清空磁碟緩衝 |
void |
然後定義了另外一個沒方法的介面DiskCache(這個介面名稱應該命名為DiscCache比較好),該介面只是簡單的繼承了DiscCacheAware介面
BaseDiscCache實現了DiskCache,該類是個抽象類別(定義為抽象類別的好處之一就是沒必要全部重寫DiskCacheAware介面提供的方法),該類定義了磁碟緩衝區的以下的(預設)屬性:
1) 預設的緩衝大小為32k
2) 預設壓縮後的圖片格式為PNG(作為Bitmap的compress方法的第一個參數)
3) 預設壓縮後圖片顯示的品質為100,也就是壓縮率為0,不進行壓縮(作為compress的第二個參數)
當然該類也提供了修改壓縮圖片格式和壓縮率以及修改緩衝大小的set方法。同時該類還封裝了以下三個屬性
protected final File cacheDir;//快取檔案的儲存Directoryprotected final File reserveCacheDir;//後備緩衝的Diectory,當cacheDir不存在的情況下就是用reserveCahceDir後備緩衝protected final FileNameGenerator fileNameGenerator;//檔案名稱名稱產生器
該類提供了三個建構函式
1) 只有一個參數的建構函式只初始化了cacheDir,沒有用到後備緩衝,且是以HashCodeFileNameGenerator來產生目標檔案的檔案名稱。
2) 兩個參數的構造器除了cacheDir和HashCodefileNameGenerator外,也可以初始化後備緩衝
3) 三個參數的構造器要求必須初始化cacheDir並且必須初始化filenNameGenerator否則就報異常
三個構造器的代碼如下
public BaseDiscCache(File cacheDir) {this(cacheDir, null);}public BaseDiscCache(File cacheDir, File reserveCacheDir) {this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator());}public BaseDiscCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {if (cacheDir == null) {throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL);}if (fileNameGenerator == null) {throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL);}this.cacheDir = cacheDir;this.reserveCacheDir = reserveCacheDir;this.fileNameGenerator = fileNameGenerator;}
BasicDiscCache實現了除close之外的其餘的六個方法,下面進行一一說明:
1) clear():迴圈便利cacheDir.listFiles()的每一個File對象,然後調用File對象的delete()方法來清空緩衝
2) getDirectory():直接返回了cacheDir
3) get(StringimageUri):該方法調用BasicCahce的一個重要方法getFile(String imageUri).
該方法如下所示(該方法在save方法中也有調用)
protected File getFile(String imageUri) {String fileName = fileNameGenerator.generate(imageUri);File dir = cacheDir;if (!cacheDir.exists() && !cacheDir.mkdirs()) {if (reserveCacheDir != null && (reserveCacheDir.exists() || reserveCacheDir.mkdirs())) {dir = reserveCacheDir;}}return new File(dir, fileName);}
4) save(StringimageUri, Bitmap bitmap)方法的具體實現
public boolean save(String imageUri, Bitmap bitmap) throws IOException {//擷取imageUri的File對象,該對象封裝了緩衝路徑和圖片儲存後的名稱File imageFile = getFile(imageUri);//擷取臨時儲存檔案的tmpFile對象File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);boolean savedSuccessfully = false;try {//調用compress把bitMap壓縮到tempFile中savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);} finally {IoUtils.closeSilently(os);//如果儲存成功並且tempFile的檔案沒有成功移動到imageFile的話,就刪除temFileif (savedSuccessfully && !tmpFile.renameTo(imageFile)) {savedSuccessfully = false;}if (!savedSuccessfully) {tmpFile.delete();}}//對bitmap進行記憶體回收bitmap.recycle();return savedSuccessfully;}
5)save的另外一個重載方法處理的邏輯同上,需要注意的是兩個save方法當檔案儲存時會先產生一個tempFile然後會將調用renameTo方法將該tempFile重新命名為imageFile
BaseDiscCache有兩個擴充類,一個是不限制緩衝大小的UnlimitedDiscCache和限制緩衝時間的LimitedAgeDiscCache其中UnlimitedDiscCache很簡單它只是簡單的繼承了BaseDiscCache並未對BaseDiscCache做任何擴充。
LimitedAgeDiscCache該類實現了在緩衝中刪除被載入超過規定時間的檔案:滿足以下條件的時候就從緩衝中刪除檔案:系統目前時間-檔案的最新修改時間》maxFileAge
該類提供了兩個屬性:
1. maxFileAge(long類型)設定載入的逾時的最大時間,改時間在構造器沖初始化,一經初始化就不能改變(設定檔案存活的最長時間,當超過這個值,就刪除該檔案)
2. loadingDates (Map),該屬性是一個map類型的對象,key儲存的要緩衝的圖片檔案,而value儲存的是調用save方法是系統的目前時間,具體向loadingDates填充資料是在下面的rememberUsage方法中實現的,該方法在類中兩個save方法中調用,首先調用父類的save方法,然後在調用此方法
private void rememberUsage(String imageUri) {File file = getFile(imageUri);long currentTime = System.currentTimeMillis();file.setLastModified(currentTime);loadingDates.put(file, currentTime);}
從緩衝中擷取資料的方法為get(String imageUri)該類是重寫BaseDiscDache方法,該方法從loadingDates中擷取imageUri所代表的圖片的最新更新時間loadingDate,然後拿目前時間和loadingDate做差,如果差值大於maxFileAge也就是說查過了載入的最大時間,就刪除該imageUri所代表的file,並從loadingDates中的資料,當然如果map中沒有imageUri就不會涉及到逾時的問題,此時就把image放入map中去,具體的實現如下
@Overridepublic File get(String imageUri) {File file = super.get(imageUri);if (file != null && file.exists()) {boolean cached;Long loadingDate = loadingDates.get(file);if (loadingDate == null) {cached = false;loadingDate = file.lastModified();} else {cached = true;}if (System.currentTimeMillis() - loadingDate > maxFileAge) {file.delete();loadingDates.remove(file);} else if (!cached) {loadingDates.put(file, loadingDate);}}return file;}