笔记73--硬盘缓存

转自郭神:http://blog.csdn.net/guolin_blog/article/details/28863651

一、简介

DiskLruCache非google官方编写,但获得官方认证。

DiskLruCache的缓存位置可自由设定,但通常指定为/sdcard/Android/data/<application package>/cache路径。选择这个路径,有两个好处:第一,这是存储在SD卡上的,因此即使缓存再多数据也不会对手机的内置存储空间有任何应用;第二,这个路径被Android系统认定为应用程序的缓存路径,当程序被卸载的时候,这里的数据也会一起被清除掉,这样就不会出现删除程序后手机上还有很多残留数据的问题。

二、下载DiskLruCache

由于它非google官方编写,所以不在android API中,需自己下载,然后手动添加到项目当中。源码地址:https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/io/DiskLruCache.java或者http://download.csdn.net/detail/sinyu890807/7709759

只需在项目中新建一个libcore.io包,然后将DiskLruCache.java文件复制到这个包中即可。

三、使用
1、打开缓存

DiskLruCache是不能new出实例的,如果要创建一个DiskLruCache的实例,则需要它的open()方法,接口如下所示:

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)  
第一个参数指定数据的缓存地址;第二个参数指定当前应用程序的版本号;第三个参数指定同一个key可以对应多少个缓存文件,基本都是传1;第四个参数指定最多可缓存多少字节的数据,通常传入10M,可根据自身情况进行调节。

虽然前面说了缓存地址,但万一手机没有SD卡,或SD卡正好被移除了的话就悲剧了。获取缓存地址:

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);  
}  
uniqueName是为了对不同类型的数据进行区分而设定的一个唯一值,比如说网易新闻缓存路径下看到的bitmap、object等文件夹。

接着是应用程序版本号,我们可用如下代码简单获取到当前应用程序的版本号:

public int getAppVersion(Context context) {  
    try {  
        PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);  
        return info.versionCode;  
    } catch (NameNotFoundException e) {  
        e.printStackTrace();  
    }  
    return 1;  
}
需要注意:每当版本号改变,缓存路径下存储的所有数据都会被清除掉,因为DiskLruCache认为当应用程序有版本更新的时候,所有的数据都应从网上重新获取。

因此,一个标准的open()方法就可这样写:

DiskLruCache mDiskLruCache = null;  
try {  
    File cacheDir = getDiskCacheDir(context, "bitmap");  
    if (!cacheDir.exists()) {  
        cacheDir.mkdirs();  
    }  
    mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);  
} catch (IOException e) {  
    e.printStackTrace();  
}  
有了DiskLruCache实例后,我们就可对缓存的数据进行操作了,操作类型主要包括写入、访问、移除等。

2、写入缓存

先来看写入,比如有一张图片,地址是:https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg,那么为了将这张图片下载下来,可以这样写:               

private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {  
    HttpURLConnection urlConnection = null;  
    BufferedOutputStream out = null;  
    BufferedInputStream in = null;  
    try {  
        final URL url = new URL(urlString);  
        urlConnection = (HttpURLConnection) url.openConnection();  
        in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);  
        out = new BufferedOutputStream(outputStream, 8 * 1024);  
        int b;  
        while ((b = in.read()) != -1) {  
            out.write(b);  
        }  
        return true;  
    } catch (final IOException e) {  
        e.printStackTrace();  
    } finally {  
        if (urlConnection != null) {  
            urlConnection.disconnect();  
        }  
        try {  
            if (out != null) {  
                out.close();  
            }  
            if (in != null) {  
                in.close();  
            }  
        } catch (final IOException e) {  
            e.printStackTrace();  
        }  
    }  
    return false;  
}  
通过outputStream写入到本地后,就可使用DiskLruCache来进行写入了。写入的操作是借助DiskLruCache.Editor这个类完成的。这个类也是不能new的,需要调用DiskLruCache的edit()方法来获取实例:

public Editor edit(String key) throws IOException
edit()接受一个参数key,这个key将会成为缓存文件的文件名,并且必须要和图片的URL是一一对应的。那么怎样才能让key和图片的URL能一一对应呢?直接使用URL作为key?不太合适,因为图片URL中可能包含一些特殊字符,这些字符有可能在命名文件时是不合法的。其实最简单的做法就是将图片的URL进行MD5编码,编码后的字符串肯定是唯一的,并且只会包含0-F这样的字符,完全符合文件的命名规则。对字符串进行MD5编码:

public String hashKeyForDisk(String key) {  
    String cacheKey;  
    try {  
        final MessageDigest mDigest = MessageDigest.getInstance("MD5");  
        mDigest.update(key.getBytes());  
        cacheKey = bytesToHexString(mDigest.digest());  
    } catch (NoSuchAlgorithmException e) {  
        cacheKey = String.valueOf(key.hashCode());  
    }  
    return cacheKey;  
}  
  
private String bytesToHexString(byte[] bytes) {  
    StringBuilder sb = new StringBuilder();  
    for (int i = 0; i < bytes.length; i++) {  
        String hex = Integer.toHexString(0xFF & bytes[i]);  
        if (hex.length() == 1) {  
            sb.append('0');  
        }  
        sb.append(hex);  
    }  
    return sb.toString();  
}  
只需调用hashKeyForDisk(),并把图片的URL传入到这个方法中,就可得到对应的key了。因此,就可这样得到一个DiskLruCache.Editor的实例:

String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
String key = hashKeyForDisk(imageUrl);  
DiskLruCache.Editor editor = mDiskLruCache.edit(key); 
有了DiskLruCache.Editor的实例后,我们可调用它的newOutputStream()来创建一个输出流,然后把它转入到downloadUrlToStream()中就能实现下载并写入缓存的功能了。注意new OutputStream()接受一个index参数,由于前面设置valueCount的时候指定的是1,所以这里index传0就可以了。在写入操作执行完之后,我们还需要调用以下commit()进行提交才能使写入生效,调用abort()则表示放弃写入。
一次完整写入操作的代码:

new Thread(new Runnable() {  
    @Override  
    public void run() {  
        try {  
            String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
            String key = hashKeyForDisk(imageUrl);  
            DiskLruCache.Editor editor = mDiskLruCache.edit(key);  
            if (editor != null) {  
                OutputStream outputStream = editor.newOutputStream(0);  
                if (downloadUrlToStream(imageUrl, outputStream)) {  
                    editor.commit();  
                } else {  
                    editor.abort();  
                }  
            }  
            mDiskLruCache.flush();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}).start(); 

3、读取缓存

读取的方法比写入要简单一些,主要是借助DiskLruCache的get()实现的。接口如下:

public synchronized Snapshot get(String key) throws IOException  
get()方法接受一个参数key来获取相应的缓存数据,而这个key无疑就是将图片URL进行MD5编码后的值了,因此读取缓存数据的代码就可这样写:

String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
String key = hashKeyForDisk(imageUrl);  
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  
这里获取到的是一个DiskLruCache.SnapShot对象,这个对象我们怎么用呢?很简单,只需调用它的getInputStream()就可得到缓存文件的输入流了。有了文件输入流后,想要把缓存图片显示到界面上就轻而易举了。所以,一段完整的读取缓存,并将图片加载到界面上的代码如下所示:

try {  
    String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
    String key = hashKeyForDisk(imageUrl);  
    DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  
    if (snapShot != null) {  
        InputStream is = snapShot.getInputStream(0);  
        Bitmap bitmap = BitmapFactory.decodeStream(is);  
        mImage.setImageBitmap(bitmap);  
    }  
} catch (IOException e) {  
    e.printStackTrace();  
}  
注意,这儿我们是从本地缓存中加载的,而不是从网络上加载的。

4、移除缓存

借助DiskLruCache的remove()实现。接口如下:

public synchronized boolean remove(String key) throws IOException 
传入一个key,然后删除这个key对应的缓存图片,代码:

try {  
    String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";    
    String key = hashKeyForDisk(imageUrl);    
    mDiskLruCache.remove(key);  
} catch (IOException e) {  
    e.printStackTrace();  
}
这个方法我们并不经常去调用它,因为你完全不需要担心缓存的数据过多从而占用SD卡太多空间的问题,DiskLruCache会根据我们在调用open()时设定的缓存最大值来自动删除多余的缓存。只有你确定某个key对应的缓存内容已经过期,需要从网络获取最新数据时才应该调用remove()来移除缓存。

四、其他API
1、size()

返回当前缓存路径下所有缓存数据的总字节数,以byte为单位。

2、flush()

用于将内存中的操作记录同步到日志文件(也就是journal文件)中。这个方法非常重要,因为DiskLruCache能正常工作的前提就是要依赖于journal文件中的内容。前面在讲解写入缓存操作时,我有调用过一次这个方法,但其实不是每次写入缓存都要调用一次flush()方法的,频繁调用并不会带来任何好处,只会额外增加同步journal文件的时间。比较标准的做法是在activity的onPause()中调用一次flush()即可。

3、close()

用于将DiskLruCache关闭,是和open()对应的一个方法。关闭只会就不能再调用DiskLruCache中任何操作缓存数据的方法,通常只应该在Activity的onDestroy()中调用close()。

4、delete()

用于将所有缓存数据删除。

五、解读journal

前面已经提过,











  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值