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; } //每一张图片对应的File public 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和sSoftBitmapCache public 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) { @Override protected 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; } else return false; } }; // Soft cache for bitmaps kicked out of hard cache private 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的Runnable private 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 cache SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url); if (bitmapReference != null) { final Bitmap bitmap = bitmapReference.get(); if (bitmap != null) { // Bitmap found in soft cache return bitmap; } else { // Soft reference has been Garbage Collected sSoftBitmapCache.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.html public 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); } @Override public 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); } @Override protected 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 // TODO if (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; } @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } // add bitmap to cache //下载完成后的操作2:将图片保存在内存中 imageCache.addBitmapToCache(url, bitmap); //下载完成后的操作3:在ImageView中显示图片 //若引用可能会被系统回收,所以要先判断imageViewWeakReference是否为null if (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的图片为下载的Bitmap if (this == bitmapDownloaderTask) { imageView.setImageBitmap(bitmap); } } } @Override protected 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) //在此得到预先指定的Drawable Drawable 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中得到一个BitmapDownloaderTask public 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 success if (bFile == true) { return true; } else { return false; } } } catch (Exception err) { err.printStackTrace(); return false; } } //判断是否可以保存 public static boolean canSave(long size) { return getAvailableExternalMemorySize() > size; } }