FileCache.java如下:
package com.cn.loadImages;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import android.content.Context;import android.net.Uri;import android.util.Log;public class FileCache {private File cacheDir;public FileCache(Context context) {if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {cacheDir = new File(android.os.Environment.getExternalStorageDirectory(),"ltcImageCache");} else {cacheDir = context.getCacheDir();}if (cacheDir != null && !cacheDir.exists()) {Utils.doMkdir(cacheDir);}}//在SD卡上建立檔案夾用來儲存圖片public FileCache(Context context, String path) {if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {cacheDir = new File(android.os.Environment.getExternalStorageDirectory()+File.separator+ path);Log.i("xx", "cacheDir="+cacheDir.toString());}if (cacheDir != null && !cacheDir.exists()) {Utils.doMkdir(cacheDir);}}//下載完成後將圖片儲存在檔案(SD卡)中public boolean addToFileCache(String url, InputStream inputStream, long size) {boolean isReturnBitmap = true;OutputStream outputStream = null;try {if (!Utils.canSave(size)) {return false;}File file = getFromFileCache(url);if (file == null) {return false;}outputStream = new FileOutputStream(file);copyStream(inputStream, outputStream);} catch (FileNotFoundException e) {e.printStackTrace();isReturnBitmap = false;} catch (Exception e) {isReturnBitmap = false;} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}return isReturnBitmap;}//每一張圖片對應的Filepublic File getFromFileCache(String url) {String fileName = getImageNameFromUrl(url);if (cacheDir == null) {return null;}File file = new File(cacheDir, fileName);return file;}//刪除所有的SD卡上的檔案快取public void clearCache() {if (cacheDir == null) {return;}File[] files = cacheDir.listFiles();if (files == null)return;for (File f : files)f.delete();}public void deleteIncompleteCache(String url) {File file = getFromFileCache(url);if (file != null && file.exists()) {file.delete();}}//從圖片的url中截取出檔案名稱private String getImageNameFromUrl(String url) { Uri uri=Uri.parse(url); String imageName=uri.getLastPathSegment(); return imageName;} //儲存圖片到SD卡時的流操作private void copyStream(InputStream inputStream, OutputStream outputStream) {final int buffer_size = 1024;try {byte[] bytes = new byte[buffer_size];for (;;) {int count = inputStream.read(bytes, 0, buffer_size);if (count == -1)break;outputStream.write(bytes, 0, count);}} catch (Exception ex) {}}}
ImageCache.java如下:
package com.cn.loadImages;import java.lang.ref.SoftReference;import java.util.HashMap;import java.util.LinkedHashMap;import java.util.concurrent.ConcurrentHashMap;import android.graphics.Bitmap;import android.os.Handler;/** * Cache-related fields and methods. * * We use a hard and a soft cache. A soft reference cache is too aggressively * cleared by the Garbage Collector. * *///這是在記憶體中的緩衝.//分為兩級sHardBitmapCache和sSoftBitmapCachepublic class ImageCache {private static final int HARD_CACHE_CAPACITY = 30;private static final int DELAY_BEFORE_PURGE = 60 * 1000; // in milliseconds// Hard cache, with a fixed maximum capacity and a life duration@SuppressWarnings("serial")private final HashMap<String, Bitmap> sHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {if (size() > HARD_CACHE_CAPACITY) {// Entries push-out of hard reference cache are transferred to soft reference cache//當sHardBitmapCache中的size大於額定容量HARD_CACHE_CAPACITY的時候//將sHardBitmapCache中最陳舊的那個對象放到了sSoftBitmapCache中//sSoftBitmapCache中的對象更容易被GC回收sSoftBitmapCache.put(eldest.getKey(),new SoftReference<Bitmap>(eldest.getValue()));return true;} elsereturn false;}};// Soft cache for bitmaps kicked out of hard cacheprivate final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);private final Handler purgeHandler = new Handler(); //用於清空(purger)sSoftBitmapCache和sHardBitmapCache的Runnableprivate final Runnable purger = new Runnable() {public void run() {//clearCache();}};//下載完成後將bitmap放在記憶體(sHardBitmapCache)中public void addBitmapToCache(String url, Bitmap bitmap) {if (bitmap != null) {synchronized (sHardBitmapCache) {sHardBitmapCache.put(url, bitmap);}}}//從imageCache中得到圖片public Bitmap getBitmapFromCache(String url) {// First try the hard reference cache// 首先希望從sHardBitmapCache中得到圖片synchronized (sHardBitmapCache) {final Bitmap bitmap = sHardBitmapCache.get(url);if (bitmap != null) {// Bitmap found in hard cache// Move element to first position, so that it is removed last// 既然現在要得到這個圖片,那麼這張圖片就是最近被使用的了.// 在所有的對象中就是最新的對象.// 所以先將該對象從sHardBitmapCache中移除// 然後將其插入到sHardBitmapCache的最前面sHardBitmapCache.remove(url);sHardBitmapCache.put(url, bitmap);return bitmap;}else{}}//如果在sHardBitmapCache中沒有,那麼可能是因為該對象太陳舊//已經放到了sSoftBitmapCache中.//所以嘗試從sSoftBitmapCache中擷取對象// Then try the soft reference cacheSoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);if (bitmapReference != null) {final Bitmap bitmap = bitmapReference.get();if (bitmap != null) {// Bitmap found in soft cachereturn bitmap;} else {// Soft reference has been Garbage CollectedsSoftBitmapCache.remove(url);}}else{}return null;}/** * Clears the image cache used internally to improve performance. Note that * for memory efficiency reasons, the cache will automatically be cleared * after a certain inactivity delay. */private void clearCache() {sHardBitmapCache.clear();sSoftBitmapCache.clear();}/** * Allow a new delay before the automatic cache clear is done. */public void resetPurgeTimer() {purgeHandler.removeCallbacks(purger);purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);}public void removeFromCache(String url) {if (sHardBitmapCache != null) {sHardBitmapCache.remove(url);}if (sSoftBitmapCache != null) {sSoftBitmapCache.remove(url);}System.gc();}}
ImageDownloader.java如下:
package com.cn.loadImages;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FilterInputStream;import java.io.IOException;import java.io.InputStream;import java.lang.ref.WeakReference;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Color;import android.graphics.drawable.ColorDrawable;import android.graphics.drawable.Drawable;import android.net.http.AndroidHttpClient;import android.os.AsyncTask;import android.widget.ImageView;//參考資料://http://blog.csdn.net/icephone/article/details/7517865//http://www.cnblogs.com/shyang--TechBlogs/archive/2011/03/24/1994080.htmlpublic class ImageDownloader {private ImageCache imageCache;private FileCache fileCache;//構造方法public ImageDownloader(Context context, String localStoragePath) {imageCache = new ImageCache();//在SD卡上建立檔案夾用來儲存圖片fileCache = new FileCache(context, localStoragePath);}/** * Download the specified image from the Internet and binds it to the * provided ImageView. The binding is immediate if the image is found in the * cache and will be done asynchronously otherwise. A null bitmap will be * associated to the ImageView if an error occurs. */public void download(String url, final ImageView imageView) {//purge 清除imageCache.resetPurgeTimer();//首先嘗試從記憶體中獲得圖片Bitmap bitmap = imageCache.getBitmapFromCache(url);if (bitmap == null) {forceDownload(url, imageView);} else {//圖片已經存在則取消該圖片潛在的下載cancelPotentialDownload(url, imageView);imageView.setImageBitmap(bitmap);}}/** * Same as download but the image is always downloaded and the cache is not * used. Kept private at the moment as its interest is not clear. *///下載圖片的方法private void forceDownload(String imageUrl, ImageView imageView) {if (imageUrl == null) {return;}if (cancelPotentialDownload(imageUrl, imageView)) {//1 建立一個BitmapDownloaderTask非同步任務// 通過BitmapDownloaderTask的構造方法可知:// 該BitmapDownloaderTask對該iamgeView進行弱引用// 注意:!!!!!!!!!!!!!!!!!!!!!// 在BitmapDownloaderTask的構造方法中// 該BitmapDownloaderTask保持了對於imageView的弱引用 // 同時在DownloadedDrawable的構造方法中// DownloadedDrawable保持了對BitmapDownloaderTask的弱引用// 所以BitmapDownloaderTask和ImageView相互弱引用形成了綁定的關係!!!!BitmapDownloaderTask bitmapDownloaderTask = new BitmapDownloaderTask(imageView);//2 建立一個DownloadedDrawable// 通過DownloadedDrawable的構造方法可知:// 該DownloadedDrawable對此bitmapDownloaderTask進行弱引用DownloadedDrawable downloadedDrawable = new DownloadedDrawable(bitmapDownloaderTask);//3 imageView顯示一個指定的顏色(Drawable)if (imageView != null) {//在圖片下載未完成的時imageView載入該downloadedDrawable//即為DownloadedDrawable中super(Color.TRANSPARENT)指定的顏色imageView.setImageDrawable(downloadedDrawable);}bitmapDownloaderTask.setUrl(imageUrl);//4 開始非同步任務bitmapDownloaderTask.execute(imageUrl);}}//取消潛在的下載private static boolean cancelPotentialDownload(String url,ImageView imageView) {BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);if (bitmapDownloaderTask != null) {String bitmapUrl = bitmapDownloaderTask.url;if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {bitmapDownloaderTask.cancel(true);} else {return false;}}return true;}/* * An InputStream that skips the exact number of bytes provided, unless it reaches EOF. */static class FlushedInputStream extends FilterInputStream {public FlushedInputStream(InputStream inputStream) {super(inputStream);}@Overridepublic long skip(long n) throws IOException {long totalBytesSkipped = 0L;while (totalBytesSkipped < n) {long bytesSkipped = in.skip(n - totalBytesSkipped);if (bytesSkipped == 0L) {int b = read();if (b < 0) {break; // we reached EOF} else {bytesSkipped = 1; // we read one byte}}totalBytesSkipped += bytesSkipped;}return totalBytesSkipped;}} //非同步任務執行下載public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {private String url;private WeakReference<ImageView> imageViewWeakReference;private Bitmap bitmap = null;private HttpClient httpClient;public String getUrl() {return this.url;}public void setUrl(String _url) {this.url = _url;}public BitmapDownloaderTask(ImageView imageView) {//在該BitmapDownloaderTask保持了對於imageView的弱引用!!!imageViewWeakReference = new WeakReference<ImageView>(imageView);}@Overrideprotected Bitmap doInBackground(String... params) {boolean download = false;// try to get image from file cache//再嘗試從檔案(SD卡)緩衝中獲得圖片File file = fileCache.getFromFileCache(url);try {if (file.exists()) {bitmap = BitmapFactory.decodeStream(new FlushedInputStream(new FileInputStream(file)));}} catch (FileNotFoundException e1) {e1.printStackTrace();bitmap = null;} catch (Exception e) {e.printStackTrace();bitmap = null;}if (bitmap != null) {download = true;return bitmap;}// end of try//從檔案(SD卡)還未能獲得圖片,那麼開始真正的下載httpClient = new DefaultHttpClient();final HttpGet getRequest = new HttpGet(url);try {HttpResponse httpResponse = httpClient.execute(getRequest);final int statusCode = httpResponse.getStatusLine().getStatusCode();if (statusCode != HttpStatus.SC_OK) {return null;}final HttpEntity httpEntity = httpResponse.getEntity();if (httpEntity != null) {InputStream inputStream = null;try {long size = httpEntity.getContentLength();inputStream = httpEntity.getContent();// save file to file cache//下載完成後的操作1:將圖片儲存在檔案(SD卡)中boolean addResult = fileCache.addToFileCache(url,inputStream, size);// end of save// TODOif (addResult) {download = true;return BitmapFactory.decodeStream(new FlushedInputStream(new FileInputStream(file)));} else {download = true;return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));}} catch (Exception e) {e.printStackTrace();} finally {if (inputStream != null) {inputStream.close();}httpEntity.consumeContent();}}} catch (Exception e) {e.printStackTrace();} finally {if (httpClient != null) {httpClient.getConnectionManager().shutdown();}if (!download) {fileCache.deleteIncompleteCache(url);}}return null;}@Overrideprotected void onPostExecute(Bitmap bitmap) {if (isCancelled()) {bitmap = null;}// add bitmap to cache//下載完成後的操作2:將圖片儲存在記憶體中imageCache.addBitmapToCache(url, bitmap);//下載完成後的操作3:在ImageView中顯示圖片//若引用可能會被系統回收,所以要先判斷imageViewWeakReference是否為nullif (imageViewWeakReference != null) {//3.1獲得任務引用的ImageView(對應於forceDownload中的1)ImageView imageView = imageViewWeakReference.get();//getBitmapDownloaderTask方法見下(core)//3.2獲得該imageview所對應的任務(對應於forceDownload中的2)BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);//3.3若當前任務為該imageview所對應的任務,則設定此imageview的圖片為下載的Bitmapif (this == bitmapDownloaderTask) {imageView.setImageBitmap(bitmap);}}}@Overrideprotected void onCancelled() {if ((httpClient instanceof AndroidHttpClient)) {((AndroidHttpClient) httpClient).close();}if (bitmap != null) {bitmap.recycle();bitmap = null;}super.onCancelled();}} /** * @param imageView Any imageView * @return Retrieve the currently active download task (if any) associated * with this imageView. null if there is no such task. */private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {if (imageView != null) {//在forceDownload的3中imageView只是顯示了一個預先指定的顏色(Drawable)//在此得到預先指定的DrawableDrawable drawable = imageView.getDrawable();if (drawable instanceof DownloadedDrawable) {DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;//因為在forceDownload的2中該DownloadedDrawable保持了對//該bitmapDownloaderTask進行弱引用//所以當然可以通過該DownloadedDrawable得到bitmapDownloaderTask//getBitmapDownloaderTask方法見下(core)return downloadedDrawable.getBitmapDownloaderTask();}}return null;}/** * A fake Drawable that will be attached to the imageView while the download * is in progress. * <p> * Contains a reference to the actual download task, so that a download task * can be stopped if a new binding is required, and makes sure that only the * last started download process can bind its result, independently of the * download finish order. * </p> *///該類包含了一個對下載任務BitmapDownloaderTask的弱引用//注意://super(Color.TRANSPARENT);//該顏色就是圖片還未載入時候ImageView所顯示的顏色static class DownloadedDrawable extends ColorDrawable {private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskWeakReference;public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {super(Color.TRANSPARENT);bitmapDownloaderTaskWeakReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);} //從WeakReference中得到一個BitmapDownloaderTaskpublic BitmapDownloaderTask getBitmapDownloaderTask() {return bitmapDownloaderTaskWeakReference.get();}} }
Utils.java如下:
package com.cn.loadImages;import java.io.File;import android.os.Environment;import android.os.StatFs;public class Utils {private static final int ERROR = -1;public static int save_dir = 1;//判斷是否已經安裝SD卡public static boolean isSDCardExist() {return android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);}//記憶體剩餘空間public static long getAvailableInternalMemorySize() {File path = Environment.getDataDirectory();StatFs stat = new StatFs(path.getPath());long blockSize = stat.getBlockSize();long availableBlocks = stat.getAvailableBlocks();return availableBlocks * blockSize;}//記憶體總空間public static long getTotalInternalMemorySize() {File path = Environment.getDataDirectory();StatFs stat = new StatFs(path.getPath());long blockSize = stat.getBlockSize();long totalBlocks = stat.getBlockCount();return totalBlocks * blockSize;}//SD卡剩餘空間public static long getAvailableExternalMemorySize() {if (isSDCardExist()) {File path = Environment.getExternalStorageDirectory();StatFs stat = new StatFs(path.getPath());long blockSize = stat.getBlockSize();long availableBlocks = stat.getAvailableBlocks();return availableBlocks * blockSize;} else {return ERROR;}}//SD卡總空間public static long getTotalExternalMemorySize() {if (isSDCardExist()) {File path = Environment.getExternalStorageDirectory();StatFs stat = new StatFs(path.getPath());long blockSize = stat.getBlockSize();long totalBlocks = stat.getBlockCount();return totalBlocks * blockSize;} else {return ERROR;}}//建立目錄public static boolean doMkdir(File dirFile) {try {boolean bFile = dirFile.exists();if (bFile == true) {return true;} else {bFile = dirFile.mkdirs();// create successif (bFile == true) {return true;} else {return false;}}} catch (Exception err) {err.printStackTrace();return false;}} //判斷是否可以儲存public static boolean canSave(long size) {return getAvailableExternalMemorySize() > size;}}