从零开始编写图片加载库(五)之图片缓存DiskCache

本文介绍了一种高效的第三方硬盘缓存方案DiskLruCache,详细解析了其使用方法及实际应用场景。通过DiskLruCache可以有效减少网络请求次数,提高图片加载速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的使用也是比较简单,提供了友好的访问接口。

  1. 打开缓存
    获取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;
    }

  1. 保存文件

    /**
     * 保存文件到硬盘
     * 
     * @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;
    }
  2. 获取文件

    /**
     * 根据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;
        }
    }
  3. 删除文件
    /**
     * 根据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);

    }

}

效果


缓存


源代码下载==》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值