二级缓存
前面我们有了一级缓存,为啥还要二级缓存呢?说白了,这就和电脑是一样的,我们电脑有内存和硬盘,内存读取速度快,所以CPU直接读取内存中的数据,但是,内存资源有限,所以我们可以把数据保存到硬盘上,这就是二级缓存,硬盘虽然读取速度慢,但是人家容量大。 Android的缓存技术也是使用了这样一个特性,总的来说,使用二级缓存的方案,就是先从一级缓存——内存中拿,没有的话,再去二级缓存——手机中拿,如果还没有,那就只能去下载了。 有了DiskLruCache,我们就可以很方便的将一部分内容缓存到手机存储中,做暂时的持久化保存,像我们经常用的一些新闻聚合类App、ZARKER等,基本都利用了DiskLruCache,浏览过的网页,即使在没有网络的情况下,也可以浏览。DiskLruCache
配置
DiskLruCache,听名字就知道是LruCache的兄弟,只不过这个应该是Google的私生子,还没有像LruCache一样添加到API中,所以我们只能去官网上下载DiskLruCache的代码,其实也就一个类。下载地址:https://developer.android.com/samples/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.html#l22在工程中使用DiskLruCache非常简单,只需要在项目中新建一个libcore.io的包,并将DiskLruCache.java文件copy过去即可。
初始化
在使用DiskLruCache之前,我们需要对缓存的目录进行下配置,DiskLruCache并不需要限定缓存保存的位置,但一般情况下,我们的缓存都保存在缓存目录下: /sdcard/Android/data/package name/cache,当然,如果没有sdcard,那么我们就使用内置存储的缓存区域:/data/data/package name/cache。 在设置好缓存目录后,我们就可以使用DiskLruCache.open方法来创建DiskLruCache:01.
File cacheDir = getFileCache(context,
"disk_caches"
);
02.
if
(!cacheDir.exists()) {
03.
cacheDir.mkdirs();
04.
}
05.
try
{
06.
mDiskCaches = DiskLruCache.open(cacheDir,
1
,
1
,
10
*
1024
*
1024
);
07.
}
catch
(IOException e) {
08.
e.printStackTrace();
09.
}
10.
11.
private
File getFileCache(Context context, String cacheFileName) {
12.
String cachePath;
13.
if
(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
14.
|| !Environment.isExternalStorageRemovable()) {
15.
cachePath = context.getExternalCacheDir().getPath();
16.
}
else
{
17.
cachePath = context.getCacheDir().getPath();
18.
}
19.
return
new
File(cachePath + File.separator + cacheFileName);
20.
}
应该不用解释了,唯一值得说的是valueCount这个参数,它是说同一个key可以对应Value的个数,一般都是1,基本没用。最后我们来看看最后返回的:
1.
return
new
File(cachePath + File.separator + cacheFileName)
这里通过cacheFileName在缓存目录下再创建一个目录是干嘛呢?这个目录是用来对不同的缓存对象进行区分的,例如images、text等等。我们可以通过size()方法来获取所有缓存数据的大小。也可以使用delete()方法来删除所有缓存。
写入缓存
权限:1.
<uses-permission android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
都说了写缓存,那读写权限肯定是不能少了。
DiskLruCache写入缓存与使用SharedPreferences方法类似,需要使用Editor对象:
1.
DiskLruCache.Editor editor = mDiskCaches.edit(key);
传入的key,就是我们需要下载的url地址,例如图片的地址,但是,url经常具有很多非法字符,这些会对我们的解析工作造成很多困难,而且,有时候我们的url地址也是需要保密的,所以我们经常通过MD5来进行url的加密,这样不仅可以加密,而且可以让所有的URL都变为规则的十六进制字符串。下面我们展示一个经典的写入缓存模板代码:
01.
String key = toMD5String(url);
02.
03.
/
04.
DiskLruCache.Editor editor = mDiskCaches.edit(key);
05.
if
(editor !=
null
) {
06.
OutputStream outputStream = editor.newOutputStream(
0
);
07.
if
(getBitmapUrlToStream(url, outputStream)) {
08.
editor.commit();
09.
}
else
{
10.
editor.abort();
11.
}
12.
}
13.
mDiskCaches.flush();
14.
/
15.
16.
public
String toMD5String(String key) {
17.
String cacheKey;
18.
try
{
19.
final
MessageDigest digest = MessageDigest.getInstance(
"MD5"
);
20.
digest.update(key.getBytes());
21.
cacheKey = bytesToHexString(digest.digest());
22.
}
catch
(NoSuchAlgorithmException e) {
23.
cacheKey = String.valueOf(key.hashCode());
24.
}
25.
return
cacheKey;
26.
}
27.
28.
private
String bytesToHexString(
byte
[] bytes) {
29.
StringBuilder sb =
new
StringBuilder();
30.
for
(
int
i =
0
; i < bytes.length; i++) {
31.
String hex = Integer.toHexString(
0xFF
& bytes[i]);
32.
if
(hex.length() ==
1
) {
33.
sb.append(
'0'
);
34.
}
35.
sb.append(hex);
36.
}
37.
return
sb.toString();
38.
}
39.
40.
private
static
boolean
getBitmapUrlToStream(String urlString, OutputStream outputStream) {
41.
HttpURLConnection urlConnection =
null
;
42.
BufferedOutputStream out =
null
;
43.
BufferedInputStream in =
null
;
44.
try
{
45.
final
URL url =
new
URL(urlString);
46.
urlConnection = (HttpURLConnection) url.openConnection();
47.
in =
new
BufferedInputStream(urlConnection.getInputStream(),
8
*
1024
);
48.
out =
new
BufferedOutputStream(outputStream,
8
*
1024
);
49.
int
b;
50.
while
((b = in.read()) != -
1
) {
51.
out.write(b);
52.
}
53.
return
true
;
54.
}
catch
(
final
IOException e) {
55.
e.printStackTrace();
56.
}
finally
{
57.
if
(urlConnection !=
null
) {
58.
urlConnection.disconnect();
59.
}
60.
try
{
61.
if
(out !=
null
) {
62.
out.close();
63.
}
64.
if
(in !=
null
) {
65.
in.close();
66.
}
67.
}
catch
(
final
IOException e) {
68.
e.printStackTrace();
69.
}
70.
}
71.
return
false
;
72.
}
这里唯一的需要注意的是,下载的方法与我们之前使用的方法有所不同,主要是为了通用性,DiskLruCache将对应URL的内容以流的形式进行存储,文件名就是MD5加密后的字符串。
读取缓存
读取缓存的方法大家应该也能想到了,自然是调用get方法:1.
DiskLruCache.Snapshot snapShot = mDiskCaches.get(key);
不过它返回的是DiskLruCache的Snapshot对象。当我们获取到了Snapshot对象,就可以从它里面获取输出流,从而取出缓存的数据:
1.
DiskLruCache.Snapshot snapShot = mDiskCaches.get(key);
2.
InputStream is = snapShot.getInputStream(
0
);
3.
Bitmap bitmap = BitmapFactory.decodeStream(is);
4.
mImageView.setImageBitmap(bitmap);
移除缓存
移除缓存,我们可以猜到,我们需要使用remove方法来实现:1.
mDiskCache.remove(key);