Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開源框架Universal-Image-Loader學習六——硬盤緩存策略

Android開源框架Universal-Image-Loader學習六——硬盤緩存策略

編輯:關於Android編程



硬盤緩存策略:

\

LimitedAgeDiscCache(設定文件存活的最長時間,當超過這個值,就刪除該文件)

UnlimitedDiscCache(這個緩存類沒有任何的限制)


繼承關系:
public class LimitedAgeDiscCache extends BaseDiscCache
public abstractclass BaseDiscCache implements DiskCache
public interface DiskCache extends DiscCacheAware
public interface DiscCacheAware

 

自底向上解析得: 1、DiscCacheAware源碼:
/** Interface for disk cache */
@Deprecated
public interface DiscCacheAware {
 
    /** 返回硬盤緩存的root directory*/
    File getDirectory();
 
    /** 返回緩存圖片的file
     * @param imageUri Original image URI
     * @return File of cached image or null - 圖片未緩存
     */
    File get(String imageUri);
 
    /**
     * 保存image bitmap到硬盤緩存中.
     * @param imageUri    Original image URI
     * @param imageStream image輸入流
     * @param listener    保存進程監聽器;在ImageLoader中不使用.core.listener.ImageLoadingProgressListener情況下可以忽略該listener
     * @return true - 保存成功; false - 保存失敗.
     * @throws IOException
     */
    boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException;
 
    /**
     * (重載)保存image bitmap到硬盤緩存中.
     * @param  imageUri - Original image URI
     * @param  bitmap   Image bitmap
     * @return true - 保存成功; false - 保存失敗.
     * @throws IOException
     */
    boolean save(String imageUri, Bitmap bitmap) throws IOException;
 
    /**
     * 根據給定URI刪除對應的image file
     * @param  imageUri - 圖片URI
     * @return true - 圖片刪除成功; 
               false- 指定URI圖片不存在或者圖片文件無法刪除
     */
    boolean remove(String imageUri);
 
    /** 關閉硬盤緩存,釋放資源. */
    void close();
 
    /** 清除硬盤緩存*/
    void clear();
}

I)上面代碼用用到IoUtils.CopyListener listener:

 

/** Listener and controller for copy process */
public static interface CopyListener {
    /**
      * @param  current 已經加載的bytes
      * @param  total   需要加載的總共的bytes
      * @return true  - 如果copying操作需要繼續進行
                false - 如果copying操作需要中斷
      */
    boolean onBytesCopied(int current, int total);
}
II)以及ImageLoadingProgressListener:

 

/** Listener for image loading progress.*/
public interface ImageLoadingProgressListener { 
    /**
     * 當加載進程改變時被調用
     * @param imageUri Image URI
     * @param view     image的View控件,可以為null.
     * @param current  已經下載的bytes大小
     * @param total    總共的bytes大小
     */
    void onProgressUpdate(String imageUri, View view, intcurrent, inttotal);
}

2、DiskCache源碼:(形式意義同MemoryCache 之於MemoryCacheAware,不過是換個名稱)

/**Interface for disk cache*/
public interface DiskCache extendsDiscCacheAware {
}

3、BaseDiscCache源碼:

/**
 * Base disk cache.
 */
public abstract class BaseDiscCache implements DiskCache {
    /** {@value */
    public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb
    public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
    public static final int DEFAULT_COMPRESS_QUALITY = 100;
 
    private static final String ERROR_ARG_NULL = " argument must be not null";
    private static final String TEMP_IMAGE_POSTFIX = ".tmp";
 
    protected final File cacheDir;
    protected final File reserveCacheDir;
    protected final FileNameGenerator fileNameGenerator;
 
    protected int bufferSize = DEFAULT_BUFFER_SIZE;// 32 Kb
    protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;//Bitmap.CompressFormat.PNG
    protected int compressQuality = DEFAULT_COMPRESS_QUALITY;//100
 
    public BaseDiscCache(File cacheDir) {
        this(cacheDir, null);
    }
 
    public BaseDiscCache(File cacheDir, File reserveCacheDir) {
        this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator());
    }
 
    /**
     * @param cacheDir          Directory for file caching
     * @param reserveCacheDir   可以為null; 
                                Reserve directory for file caching. It's used when the primary directory isn't available.
     * @param fileNameGenerator FileNameGenerator(Generates names for files at disk cache) for cached files
     */
    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;
    }
 
    @Override
   /** 重寫DiscCacheAware.getDirectory()*/
    public File getDirectory() {
        return cacheDir;
    }
 
    @Override
    /** 重寫DiscCacheAware.get()*/
    public File get(String imageUri) {
        return getFile(imageUri);
    }
 
    @Override
    /**保存image bitmap到硬盤緩存中.參數含義見DisCacheAware*/
    public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
        File imageFile = getFile(imageUri);//根據imageUri獲取相關File
        File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);//定義為.tmp文件
        boolean loaded = false;//加載標志
        try {
            OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);//bufferSize=32 Kb
            try {
                loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize);//imageStream寫入os,見注釋III
            } finally {
                IoUtils.closeSilently(os); //釋放資源,見注釋IV
            }
        } finally {
            IoUtils.closeSilently(imageStream);
            if (loaded && !tmpFile.renameTo(imageFile)) {//見注釋V
                loaded = false;
            }
            if (!loaded) {
                tmpFile.delete();//失敗注意釋放資源
            }
        }
        return loaded;
    }
 
    @Override
    /** 保存image bitmap到硬盤緩存中,沒有IoUtils.CopyListener情況*/
    public boolean save(String imageUri, Bitmap bitmap) throws IOException {
        File imageFile = getFile(imageUri);
        File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
        OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
        boolean savedSuccessfully = false;
        try {
            savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);//圖片壓縮
        } finally {
            IoUtils.closeSilently(os);
            if (savedSuccessfully && !tmpFile.renameTo(imageFile)) {
                savedSuccessfully = false;
            }
            if (!savedSuccessfully) {
                tmpFile.delete();
            }
        }
        bitmap.recycle();
        return savedSuccessfully;
    }
 
    @Override
    public boolean remove(String imageUri) {
        return getFile(imageUri).delete();
    }
 
    @Override
    public void close() {
        // Nothing to do
    }
 
    @Override
    public void clear() {
        File[] files = cacheDir.listFiles();
        if (files != null) {
            for (File f : files) {
                f.delete();
            }
        }
    }
 
    /** 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);//Constructs a new file using the specified directory and name.
    }
 
    public void setBufferSize(intbufferSize) {
        this.bufferSize = bufferSize;
    }
 
    public void setCompressFormat(Bitmap.CompressFormat compressFormat) {
        this.compressFormat = compressFormat;
    }
 
    public void setCompressQuality(intcompressQuality) {
        this.compressQuality = compressQuality;
    }
}

I)用到的CompressFormat.PNG枚舉類

/** Specifies the known formats a bitmap can be compressed into*/
public enum CompressFormat {
    JPEG    (0),
    PNG     (1),
    WEBP    (2);
 
    CompressFormat(int nativeInt) {
        this.nativeInt = nativeInt;
    }
    final int nativeInt;
}

II)工具類:FileNameGenerator

/** Generates names for files at disk cache*/
public interface FileNameGenerator {
    /** Generates unique file name for image defined by URI */
    String generate(String imageUri);
}

III)IoUtils.copyStream(imageStream, os, listener, bufferSize);

/**
     * 拷貝stream, fires progress events by listener, can be interrupted by listener.
     *
     * @param is         Input stream
     * @param os         Output stream
     * @param listener   可以為null; 拷貝進程的Listener以及拷貝中斷的控制器controller
     * @param bufferSize copying的Buffer Size;也代表了每一次觸發progress listener callback回調的“一步”
                         ————即每次copied bufferSize個bytes大小後即觸發progress event
     * @returntrue -    stream拷貝成功; false - 拷貝操作被listener中斷
     * @throws IOException
     */
    public static boolean copyStream(InputStream is, OutputStream os, CopyListener listener, int bufferSize)
            throws IOException {
        int current = 0;
        int total = is.available();
        if (total <= 0) {
            total = DEFAULT_IMAGE_TOTAL_SIZE;
        }
 
        final byte[] bytes = new byte[bufferSize];
        int count;
        if (shouldStopLoading(listener, current, total)) return false;
        while ((count = is.read(bytes, 0, bufferSize)) != -1) {
            os.write(bytes, 0, count);//寫入os中
            current += count;         //更新當前加載的bytes數
            if (shouldStopLoading(listener, current, total)) return false;
        }
        os.flush();
        return true;
    }

   private static boolean shouldStopLoading(CopyListener listener, int current, int total) {
        if (listener != null) {
            boolean shouldContinue = listener.onBytesCopied(current, total);//參加上面CopyListener
            if (!shouldContinue) {
                if (100 * current / total < CONTINUE_LOADING_PERCENTAGE) {
                    return true; // 當加載超過75%,則直接加載,不中斷;否則,return true,產生中斷
                }
            }
        }
        return false;
    }

IV)IoUtils.closeSilently()方法

 

 

 

 

 publicstaticvoid closeSilently(Closeable closeable) {
        try {
            closeable.close();
        } catch (Exception e) {
            // Do nothing
        }
    }

 

下面分析JDK中的Closeable :

比如InputStream,OutputStream都實現了 Closeable接口

public abstract class InputStream extends Object implementsCloseable
public abstract class OutputStream implements Closeable, Flushable

AutoCloseable源碼:

package java.lang;
/**
 * 定義一個interface for 那些一旦不再使用就可以(或者需要)被關閉的classes
 *  一般用法:
 *   Closable foo = new Foo();
 *   try {
 *      ...;
 *   } finally {
 *      foo.close();
 *   }
 * }
 */
public interface AutoCloseable {
    /** Close 相應 Object 並釋放它所持有的所有系統資源(system resources)*/
    void close() throws Exception;
}

Closeable源碼:

package java.io;
public interface Closeable extends AutoCloseable {
 
    /**
     * 與AutoCloseable區別:雖然只有第一次call會產生有效作用,但本close方法
     * 在同一個object上被多次調用是安全的。而 AutoCloseable.close()最多只能被調用一次
     */
    void close() throws IOException;
}

V) File.renameTo()方法

 

 /**
     * Renames this file to {@code newPath}. 該操作支持files 和 directories
     * 此操作有很多導致failures的方法,包括:
     * (1) 寫權限(Write permission)Write permission is required on the directories containing both the source and
     * destination paths.
     * (2) 搜索權限(Search permission) is required for all parents of both paths.
     */
    public boolean renameTo(File newPath) {
        try {
            Libcore.os.rename(path, newPath.path);
            return true;
        } catch (ErrnoException errnoException) {
            return false;
        }
    }


 

4、LimitedAgeDiscCache 源碼:

/**
 * 時間策略,刪除最早加載即loaded的時間超過限定時間的文件. Cache size是無限制的.
 */
public class LimitedAgeDiscCache extends BaseDiscCache {
 
    private final long maxFileAge;
 
    private final Map loadingDates = Collections.synchronizedMap(new HashMap());
 
    public LimitedAgeDiscCache(File cacheDir, long maxAge) {
        this(cacheDir, null, DefaultConfigurationFactory.createFileNameGenerator(), maxAge);
    }

    public LimitedAgeDiscCache(File cacheDir, File reserveCacheDir, long maxAge) {
        this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator(), maxAge);
    }
 
    /**
     * @param cacheDir          Directory for file caching
     * @param reserveCacheDir   可為null; Reserve directory for file caching. It's used when the primary directory isn't available.
     * @param fileNameGenerator Name generator for cached files
     * @param maxAge            Max file age (in seconds). If file age will exceed this value then it'll be removed on next
     *                          treatment (and therefore be reloaded).
     */
    public LimitedAgeDiscCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long maxAge) {
        //調用public BaseDiscCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator)
        super(cacheDir, reserveCacheDir, fileNameGenerator);
        this.maxFileAge = maxAge * 1000; // 轉化為milliseconds
    }
 
    @Override
    public File get(String imageUri) {
        File file = super.get(imageUri);
        if (file != null && file.exists()) {
            boolean cached;
            Long loadingDate = loadingDates.get(file);//Map loadingDates
            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;
    }
 
    @Override
    public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
        boolean saved = super.save(imageUri, imageStream, listener);
        rememberUsage(imageUri);//更新相關文件的最新修改時間
        return saved;
    }
 
    @Override
    public boolean save(String imageUri, Bitmap bitmap) throws IOException {
        boolean saved = super.save(imageUri, bitmap);
        rememberUsage(imageUri);
        return saved;
    }
 
    @Override
    public boolean remove(String imageUri) {
        loadingDates.remove(getFile(imageUri));
        return super.remove(imageUri);
    }
 
    @Override
    public void clear() {
        super.clear();
        loadingDates.clear();
    }
 
    private void rememberUsage(String imageUri) {
        File file = getFile(imageUri);
        long currentTime = System.currentTimeMillis();
        file.setLastModified(currentTime);
        loadingDates.put(file, currentTime);
    }
}

5、UnlimitedDiscCache 源碼:

 

/**
 * UIL框架中默認的DiskCache實現,Cache size是無限制的
 */
public class UnlimitedDiscCache extends BaseDiscCache {

    public UnlimitedDiscCache(File cacheDir) {
        super(cacheDir);
    }

    public UnlimitedDiscCache(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 UnlimitedDiscCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
        super(cacheDir, reserveCacheDir, fileNameGenerator);
    }
}


 

 

 

 

 

 



 

 

 

 

 

 

 

 

 

 




 

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved