1.写在前面
在处理图片比较多的应用中,为了提高程序的响应速率,不可避免的需要硬盘缓存技术和内存缓存技术结合使用,根据图片地址去请求网络的时候,先请求内存,如果内存没有再去请求本地缓存,如果本地缓存 没有再请求网络。这是一般图片加载框架的处理思路。
2.DiskLruCache的介绍
DiskLruCache是什么?DiskLruCache是一款优秀的第三方硬盘缓存解决方案,我们熟知的网易新闻就是通过这个来缓存图片到本地。
网易新闻需要加载的图片还是挺多的,如果每次进入app都需要请求网络加载图片显然是不合适的,一方面流量的消耗还有就是性能影响,那么保存到本地是一个很好的解决方案。那么网易是怎么保存到本地的呢?
DiskLruCache的缓存目录是可以自己定义的,但是一般我们都将缓存文件保存在 /sdcard/Android/data//cache ,但是假如手机没有外置内存卡,那么就将取不到缓存路径,所以好的解决方法就是:
public File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
2.1DiskLruCache的使用
前面絮叨半天,进入正题吧,DiskLruCache的使用也是比较简单,提供了友好的访问接口。
- 打开缓存
获取DiskLruCache的方法
/**
* Opens the cache in {@code directory}, creating a cache if none exists
* there.
*
* @param directory a writable directory
* @param valueCount the number of values per cache entry. Must be positive.
* @param maxSize the maximum number of bytes this cache should use to store
* @param maxFileCount the maximum file count this cache should store
* @throws IOException if reading or writing the cache directory fails
*/
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount)
throws IOException {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
if (maxFileCount <= 0) {
throw new IllegalArgumentException("maxFileCount <= 0");
}
if (valueCount <= 0) {
throw new IllegalArgumentException("valueCount <= 0");
}
// If a bkp file exists, use it instead.
File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
if (backupFile.exists()) {
File journalFile = new File(directory, JOURNAL_FILE);
// If journal file also exists just delete backup file.
if (journalFile.exists()) {
backupFile.delete();
} else {
renameTo(backupFile, journalFile, false);
}
}
// Prefer to pick up where we left off.
DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount);
if (cache.journalFile.exists()) {
try {
cache.readJournal();
cache.processJournal();
cache.journalWriter = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(cache.journalFile, true), Util.US_ASCII));
return cache;
} catch (IOException journalIsCorrupt) {
System.out
.println("DiskLruCache "
+ directory
+ " is corrupt: "
+ journalIsCorrupt.getMessage()
+ ", removing");
cache.delete();
}
}
// Create a new empty cache.
directory.mkdirs();
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount);
cache.rebuildJournal();
return cache;
}
保存文件
/** * 保存文件到硬盘 * * @param key * @param imageStream * @return * @throws IOException */ public boolean save(String key, InputStream imageStream) throws IOException { DiskLruCache.Editor editor = cache.edit(key); if (editor == null) { return false; } OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), DEFAULT_BUFFER_SIZE); int count; byte[] bs = new byte[DEFAULT_BUFFER_SIZE]; while ((count = imageStream.read(bs, 0, DEFAULT_BUFFER_SIZE)) != -1) { os.write(bs, 0, count); } os.flush(); editor.commit(); return true; }
获取文件
/** * 根据key去获取文件存储目录 * * @param key * @return */ public File get(String key) { DiskLruCache.Snapshot snapshot = null; try { snapshot = cache.get(key); return snapshot == null ? null : snapshot.getFile(0); } catch (IOException e) { e.printStackTrace(); return null; } }
- 删除文件
/**
* 根据key去删除文件
* @param key
* @return
*/
public boolean remove(String key) {
try {
return cache.remove(key);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
2.2.DiskLruCache的实际应用效果
为了演示DiskLruCache的效果,建了一个项目,通过网络下载图片,将获取的文件保存到本地,通过Handler获取更新UI通知,根据下载的图片的key去本地查找图片并且显示。
package cn.sundroid.loader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import cn.sundroid.cache.ext.LruDiskCache;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;
public class MainActivity extends Activity {
private ImageView image;
private final String TAG = "ImageLoader";
private int cacheMaxSize = 10 * 1024 * 1024; //10M
LruDiskCache cache;
private final String IMAGE_CACHE_KEY = "image_cache_key";
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
File file = cache.get(IMAGE_CACHE_KEY);
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
image.setImageBitmap(bitmap);
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File cacheDir = getDiskCacheDir(getApplicationContext(), "imageloader");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
try {
//硬盘初始化
cache = new LruDiskCache(cacheDir, cacheMaxSize, 0);
} catch (IOException e1) {
e1.printStackTrace();
}
image = (ImageView) findViewById(R.id.image);
//开启一个新线程
new Thread(new Runnable() {
@Override
public void run() {
try {
// 网络连接
HttpURLConnection connection = (HttpURLConnection) new URL(
"http://img4.duitang.com/uploads/item/201111/25/20111125231822_MCxG5.thumb.600_0.jpg")
.openConnection();
connection.setConnectTimeout(3 * 1000);
connection.setReadTimeout(20 * 1000);
// 读取文件流
InputStream is = connection.getInputStream();
Log.e(TAG, "put InputStream into memory cache");
cache.save(IMAGE_CACHE_KEY, is);
// 发送一个消息给Handler用于更新消息
handler.sendEmptyMessage(0x10);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
//获取缓存路径
public File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
}