android DiskLruCache使用方法

来源:互联网 发布:ospf链路状态数据库 编辑:程序博客网 时间:2024/06/06 11:03

下载

好了,对DiskLruCache有了最初的认识之后,下面我们来学习一下DiskLruCache的用法吧。由于DiskLruCache并不是由Google官方编写的,所以这个类并没有被包含在Android API当中,我们需要将这个类从网上下载下来,然后手动添加到项目当中。DiskLruCache的源码在Google Source上,地址在文章底部,下载好了源码之后,只需要在项目中新建一个libcore.io包,然后将DiskLruCache.java文件复制到这个包中即可。

打开缓存

这样的话我们就把准备工作做好了,下面看一下DiskLruCache到底该如何使用。首先你要知道,DiskLruCache是不能new出实例的,如果我们要创建一个DiskLruCache的实例,则需要调用它的open()方法,接口如下所示:

1

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)

open()方法接收四个参数,第一个参数指定的是数据的缓存地址,第二个参数指定当前应用程序的版本号,第三个参数指定同一个key可以对应多少个缓存文件,基本都是传1,第四个参数指定最多可以缓存多少字节的数据。

其中缓存地址前面已经说过了,通常都会存放在 /sdcard/Android/data/<application package>/cache 这个路径下面,但同时我们又需要考虑如果这个手机没有SD卡,或者SD正好被移除了的情况,因此比较优秀的程序都会专门写一个方法来获取缓存地址,如下所示:

1

2

3

4

5

6

7

8

9

10

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

}

可以看到,当SD卡存在或者SD卡不可被移除的时候,就调用getExternalCacheDir()方法来获取缓存路径,否则就调用getCacheDir()方法来获取缓存路径。前者获取到的就是 /sdcard/Android/data/<application package>/cache 这个路径,而后者获取到的是 /data/data/<application package>/cache 这个路径。

接着又将获取到的路径和一个uniqueName进行拼接,作为最终的缓存路径返回。那么这个uniqueName又是什么呢?其实这就是为了对不同类型的数据进行区分而设定的一个唯一值,比如说在网易新闻缓存路径下看到的bitmapobject等文件夹。

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

1

2

3

4

5

6

7

8

9

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认为当应用程序有版本更新的时候,所有的数据都应该从网上重新获取。

后面两个参数就没什么需要解释的了,第三个参数传1,第四个参数通常传入10M的大小就够了,这个可以根据自身的情况进行调节。

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


1

2

3

4

5

6

7

8

9

10

DiskLruCache mDiskLruCache = null;  

try {  

    File cacheDir = getDiskCacheDir(context, "bitmap");  

    if (!cacheDir.exists()) {  

        cacheDir.mkdirs();  

    }  

    mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 110 1024 1024);  

catch (IOException e) {  

    e.printStackTrace();  

}

首先调用getDiskCacheDir()方法获取到缓存地址的路径,然后判断一下该路径是否存在,如果不存在就创建一下。接着调用DiskLruCacheopen()方法来创建实例,并把四个参数传入即可。

有了DiskLruCache的实例之后,我们就可以对缓存的数据进行操作了,操作类型主要包括写入、访问、移除等,我们一个个进行学习。

写入缓存

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

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;  

}

这段代码相当基础,相信大家都看得懂,就是访问urlString中传入的网址,并通过outputStream写入到本地。有了这个方法之后,下面我们就可以使用DiskLruCache来进行写入了,写入的操作是借助DiskLruCache.Editor这个类完成的。类似地,这个类也是不能new的,需要调用DiskLruCacheedit()方法来获取实例,接口如下所示:

双击代码复制

1

public Editor edit(String key) throws IOException

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

那么我们就写一个方法用来将字符串进行MD5编码,代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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的实例:

1

2

3

String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  

String key = hashKeyForDisk(imageUrl);  

DiskLruCache.Editor editor = mDiskLruCache.edit(key);

有了DiskLruCache.Editor的实例之后,我们可以调用它的newOutputStream()方法来创建一个输出流,然后把它传入到downloadUrlToStream()中就能实现下载并写入缓存的功能了。注意newOutputStream()方法接收一个index参数,由于前面在设置valueCount的时候指定的是1,所以这里index0就可以了。在写入操作执行完之后,我们还需要调用一下commit()方法进行提交才能使写入生效,调用abort()方法的话则表示放弃此次写入。

因此,一次完整写入操作的代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

new Thread(new Runnable() {  

    @Override 

    public void run() {  

        try {  

            String imageUrl = "http://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();

由于这里调用了downloadUrlToStream()方法来从网络上下载图片,所以一定要确保这段代码是在子线程当中执行的。注意在代码的最后我还调用了一下flush()方法,这个方法并不是每次写入都必须要调用的,但在这里却不可缺少,我会在后面说明它的作用。

现在的话缓存应该是已经成功写入了,我们进入到SD卡上的缓存目录里看一下,如下图所示:

可以看到,这里有一个文件名很长的文件,和一个journal文件,那个文件名很长的文件自然就是缓存的图片了,因为是使用了MD5编码来进行命名的。

读取缓存

缓存已经写入成功之后,接下来我们就该学习一下如何读取了。读取的方法要比写入简单一些,主要是借助DiskLruCacheget()方法实现的,接口如下所示:

1

public synchronized Snapshot get(String key) throws IOException

很明显,get()方法要求传入一个key来获取到相应的缓存数据,而这个key毫无疑问就是将图片URL进行MD5编码后的值了,因此读取缓存数据的代码就可以这样写:

1

2

3

String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  

String key = hashKeyForDisk(imageUrl);  

DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);

很奇怪的是,这里获取到的是一个DiskLruCache.Snapshot对象,这个对象我们该怎么利用呢?很简单,只需要调用它的getInputStream()方法就可以得到缓存文件的输入流了。同样地,getInputStream()方法也需要传一个index参数,这里传入0就好。有了文件的输入流之后,想要把缓存图片显示到界面上就轻而易举了。所以,一段完整的读取缓存,并将图片加载到界面上的代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

try {  

    String imageUrl = "http://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();  

}

 

我们使用了BitmapFactorydecodeStream()方法将文件流解析成Bitmap对象,然后把它设置到ImageView当中。如果运行一下程序,将会看到如下效果:

OK,图片已经成功显示出来了。注意这是我们从本地缓存中加载的,而不是从网络上加载的,因此即使在你手机没有联网的情况下,这张图片仍然可以显示出来。

移除缓存

学习完了写入缓存和读取缓存的方法之后,最难的两个操作你就都已经掌握了,那么接下来要学习的移除缓存对你来说也一定非常轻松了。移除缓存主要是借助DiskLruCacheremove()方法实现的,接口如下所示:

1

public synchronized boolean remove(String key) throws IOException

相信你已经相当熟悉了,remove()方法中要求传入一个key,然后会删除这个key对应的缓存图片,示例代码如下:

1

2

3

4

5

6

7

try {  

    String imageUrl = "http://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

除了写入缓存、读取缓存、移除缓存之外,DiskLruCache还提供了另外一些比较常用的API,我们简单学习一下。

1. size()

这个方法会返回当前缓存路径下所有缓存数据的总字节数,以byte为单位,如果应用程序中需要在界面上显示当前缓存数据的总大小,就可以通过调用这个方法计算出来。比如网易新闻中就有这样一个功能,如下图所示:

2.flush()

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

3.close()

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

4.delete()

这个方法用于将所有的缓存数据全部删除,比如说网易新闻中的那个手动清理缓存功能,其实只需要调用一下DiskLruCachedelete()方法就可以实现了。


disklrucache


0 0