Android UIL圖片加載緩存源碼分析-硬盤緩存

上面一篇文章《Android UIL圖片加載緩存源碼分析-內存緩存》咱們已經分析了Android著名的圖片加載庫UIL的內存緩存模型,本篇文章咱們接着分析另一種緩存方式-磁盤緩存,磁盤緩存說到底就是將圖片緩存到本地SD卡中,咱們經過UIL的磁盤緩存來分析一下。java

源碼環境

版本:V1.9.5
GitHub連接地址:https://github.com/nostra13/Android-Universal-Image-Loader
複製代碼

DiskCache.java (接口)

/**
 * Interface for disk cache
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.9.2
 */
public interface DiskCache {
	/**
	 * Returns root directory of disk cache
	 * 返回磁盤緩存的根目錄
	 * @return Root directory of disk cache
	 */
	File getDirectory();
	/**
	 * Returns file of cached image
	 * 經過imageUri返回文件類型的bitmap
	 * @param imageUri Original image URI
	 * @return File of cached image or <b>null</b> if image wasn't cached */ File get(String imageUri); /** * 緩存圖片 * Saves image stream in disk cache. * Incoming image stream shouldn't be closed in this method.
	 *
	 * @param imageUri    Original image URI
	 * @param imageStream Input stream of image (shouldn't be closed in this method) * @param listener Listener for saving progress, can be ignored if you don't use
	 *                    {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
	 *                    progress listener} in ImageLoader calls
	 * @return <b>true</b> - if image was saved successfully; <b>false</b> - if image wasn't saved in disk cache. * @throws java.io.IOException */ boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException; /** * Saves image bitmap in disk cache. * * @param imageUri Original image URI * @param bitmap Image bitmap * @return <b>true</b> - if bitmap was saved successfully; <b>false</b> - if bitmap wasn't saved in disk cache.
	 * @throws IOException
	 */
	boolean save(String imageUri, Bitmap bitmap) throws IOException;
	/**
	 * 移除圖片
	 * Removes image file associated with incoming URI
	 *
	 * @param imageUri Image URI
	 * @return <b>true</b> - if image file is deleted successfully; <b>false</b> - if image file doesn't exist for * incoming URI or image file can't be deleted.
	 */
	boolean remove(String imageUri);
	/** Closes disk cache, releases resources. */
	void close();
	/** 
	* 清空磁盤中的緩存
	* Clears disk cache. */
	void clear();
}
複製代碼

同內存緩存圖片同樣,磁盤緩存也設計了一個基礎的接口,各類不一樣方式的磁盤緩存方式實現該接口來實現不一樣的緩存策略。git

咱們來分析一下UIL默認的磁盤緩存策略,UnlimitedDiskCache,該緩存方式是UIL的默認緩存方式,咱們分析一下源碼能夠發現:github

UnlimitedDiskCache.java (類,默認緩存方式)

/**
 * Default implementation of {@linkplain com.nostra13.universalimageloader.cache.disc.DiskCache disk cache}.
 * Cache size is unlimited.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.0.0
 */
public class UnlimitedDiskCache extends BaseDiskCache {
	/** @param cacheDir Directory for file caching */
	public UnlimitedDiskCache(File cacheDir) {
		super(cacheDir);
	}
	/**
	 * @param cacheDir        Directory for file caching
	 * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
	 */
	public UnlimitedDiskCache(File cacheDir, File reserveCacheDir) {
		super(cacheDir, reserveCacheDir);
	}
	/**
	 * @param cacheDir          Directory for file caching
	 * @param reserveCacheDir   null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
	 * @param fileNameGenerator {@linkplain com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator
	 *                          Name generator} for cached files
	 */
	public UnlimitedDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
		super(cacheDir, reserveCacheDir, fileNameGenerator);
	}
}
複製代碼

咱們發現該緩存方式比較簡單,只有幾個構造函數,咱們發現它是集成BaseDiskCache的,因此咱們接着分析BaseDiskCache的源代碼。緩存

BaseDiskCache.java (類)

/**
 * 基礎的磁盤緩存,抽象類
 * Base disk cache.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @see FileNameGenerator
 * @since 1.0.0
 */
public abstract class BaseDiskCache implements DiskCache {
	/** {@value  默認的緩存大小32Kb*/
	public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb
	/** {@value 默認的png圖片格式*/
	public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
	
	/** {@value 壓縮質量100,不壓縮*/
	public static final int DEFAULT_COMPRESS_QUALITY = 100;
	private static final String ERROR_ARG_NULL = " argument must be not null";
	//圖片臨時後綴tmp
	private static final String TEMP_IMAGE_POSTFIX = ".tmp";
	
	//文件緩存路徑
	protected final File cacheDir;
	//備用的文件緩存目錄,能夠爲null。它只有當cacheDir不能用的時候纔有用。
	protected final File reserveCacheDir;
	
	//文件名生成器。爲緩存的文件生成文件名。
	protected final FileNameGenerator fileNameGenerator;
	protected int bufferSize = DEFAULT_BUFFER_SIZE;
	protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
	protected int compressQuality = DEFAULT_COMPRESS_QUALITY;
	//....
}
複製代碼

接下來咱們看下該類的構造器bash

/** @param cacheDir Directory for file caching */
public BaseDiskCache(File cacheDir) {
	this(cacheDir, null);
}
/**
 * 只有當設置的文件路徑不能用時,咱們才使用備份的文件目錄
 * @param cacheDir        Directory for file caching
 * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
 */
public BaseDiskCache(File cacheDir, File reserveCacheDir) {
	//使用默認的文件名生成器
	this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator());
}
/**
 * @param cacheDir          Directory for file caching
 * @param reserveCacheDir   null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
 * @param fileNameGenerator {@linkplain com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator
 *                          Name generator} for cached files
 */
public BaseDiskCache(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;
}
複製代碼

在第二個構造函數中,咱們看到了生成了默認的文件名稱生成器,咱們來具體看看裏面如何運做的。ide

//DefaultConfigurationFactory.java
/** Creates {@linkplain HashCodeFileNameGenerator default implementation} of FileNameGenerator */
	public static FileNameGenerator createFileNameGenerator() {
		return new HashCodeFileNameGenerator();
	}
//HashCodeFileNameGenerator.java
/**
 * Names image file as image URI {@linkplain String#hashCode() hashcode}
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.3.1
 */
public class HashCodeFileNameGenerator implements FileNameGenerator {
	@Override
	public String generate(String imageUri) {
		return String.valueOf(imageUri.hashCode());
	}
}
複製代碼

看到沒?很是簡單,就是一個imageURI的hash碼,真的是你意想不到的簡單,就是運用String.hashCode()進行文件名的生成。咱們看完了圖片磁盤緩存的命名策略後繼續分析下面的代碼:函數

@Override
public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
	File imageFile = getFile(imageUri);
	File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
	boolean loaded = false;
	try {
		OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
		try {
			loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize);
		} finally {
			IoUtils.closeSilently(os);
		}
	} finally {
		if (loaded && !tmpFile.renameTo(imageFile)) {
			loaded = false;
		}
		if (!loaded) {
			tmpFile.delete();
		}
	}
	return loaded;
}
複製代碼

其中涉及到一個方法getFile,咱們具體看下什麼內容:源碼分析

/** 它主要用於生成一個指向緩存目錄中的文件
* Returns file object (not null) for incoming image URI. File object can reference to non-existing file. */
	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);
	}
複製代碼

注意save方法中第3行的tmpFile,它是用來寫入bitmap的臨時文件(見第8行),而後就把這個文件給刪除了。在getFile方法中,讓咱們來分析一下這個在以前碰過的函數。 第2行就是利用fileNameGenerator生成一個惟一的文件名。第3~8行是指定緩存目錄,這時候你就能夠清楚地看到cacheDir和reserveCacheDir之間的關係了,當cacheDir不可用的時候,就是用reserveCachedir做爲緩存目錄了。最後返回一個指向文件的對象,可是要注意當File類型的對象指向的文件不存在時,file會爲null,而不是報錯。學習

磁盤緩存總結

如今,咱們已經分析了UIL的緩存機制。其實從UIL的緩存機制的實現並非很複雜,雖然有各類緩存機制,可是簡單地說:內存緩存其實就是利用Map接口的對象在內存中進行緩存,可能有不一樣的存儲機制。磁盤緩存其實就是將文件寫入磁盤。ui

  1. FileCountLimitedDiscCache(能夠設定緩存圖片的個數,當超過設定值,刪除掉最早加入到硬盤的文件,最新版本已刪除)
  2. LimitedAgeDiscCache(設定文件存活的最長時間,當超過這個值,就刪除該文件)
  3. TotalSizeLimitedDiscCache(設定緩存bitmap的最大值,當超過這個值,刪除最早加入到硬盤的文件,最新版本已刪除)
  4. UnlimitedDiscCache(這個緩存類沒有任何的限制)
  5. LruDiskCache (最近最少使用磁盤緩存,也使用了內存緩存相似的相關策略)

在UIL中有着比較完整的存儲策略,根據預先指定的空間大小,使用頻率(生命週期),文件個數的約束條件,都有着對應的實現策略。最基礎的接口DiscCacheAware和抽象類BaseDiscCache

關於做者

專一於 Android 開發多年,喜歡寫 blog 記錄總結學習經驗,blog 同步更新於本人的公衆號,歡迎你們關注,一塊兒交流學習~

在這裏插入圖片描述
相關文章
相關標籤/搜索