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;
}
}