android--(图片 双缓存实例)

本文深入探讨了内存缓存与磁盘缓存工具类的实现原理,包括单例模式提供工具类实例,磁盘缓存的打开与配置,缓存目录获取,应用程序版本获取,以及MD5摘要计算等关键功能。

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

/**
 * 内存缓存、磁盘缓存 工具类
 */
public class LruCacheUtils {

    private static LruCacheUtils lruCacheUtils;
    private Context context;

    //磁盘缓存
    private DiskLruCache diskLruCache;

    //内存缓存
    private LruCache<String, Bitmap> lruCache;

    private Bitmap bitmap;


    private LruCacheUtils() {
    }

    ;

    /**
     * 单例模式 提供 工具类的实例
     */

    public static LruCacheUtils getInstance() {
        if (lruCacheUtils == null) {
            lruCacheUtils = new LruCacheUtils();
        }
        return lruCacheUtils;
    }


    /**
     * 打开 磁盘缓存
     *
     * @param context
     * @param disk_cache_subdir :缓存子目录
     * @param disk_cache_size   :缓存大小
     */
    public void open(Context context, String disk_cache_subdir, int disk_cache_size) {
        try {
            this.context = context;

            //获取当前activity的内存大小
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            int memoryClass = am.getMemoryClass();

            //计算缓存的大小 字节 8/1 的内存作为缓存大小
            final int cacheSize = memoryClass / 8 * 1024 * 1024;

            //创建磁盘缓存
            lruCache = new LruCache<>(cacheSize);


            /***
             *  open()方法 接收四个参数,
             *  第一个参数指定的是数据的缓存地址
             *  第二个参数指定当前应用程序的版本号,作用:当版本更新时 缓存替换掉
             *  第三个参数 指定一个key 可以对应多个缓存文件,基本都是传1 :同一个缓存只能缓存一个
             *  第四个参数指定最多可以缓存多少字节的数据,通常10MB
             *  */
            diskLruCache = diskLruCache.open(getCacheDir(disk_cache_subdir), getAppVersion(), 1, disk_cache_size);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取缓存 目录
     *
     * @param name :缓存文件
     * @return sdcard 上的 私有目录
     * context.getExternalCacheDir().getPath() :在 mnt/sdcard/android/data/应用程序包/cache文件夹路径
     * <p/>
     * 安装应用程序下的 私有目录
     * context.getCacheDir().getPath() :在 data/data/应用程序包/cache
     */
    private File getCacheDir(String name) {

        String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
                || !Environment.isExternalStorageRemovable() ?
                context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();

        return new File(cachePath + File.separator + name);
    }


    /**
     * 获取当前应用程序的版本
     */
    public int getAppVersion() {
        try {
            return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }


    /**
     * 计算字符 MD5 摘要
     *
     * @param key
     * @return
     */
    private String hashkeyForDisk(String key) {

        String cacheKey;
        try {
            final MessageDigest messageDigest = MessageDigest.getInstance("MD5");

            messageDigest.update(key.getBytes());

            //将字符串摘要 计算为 十六进制的 字符串 作为 key
            cacheKey = bytestoHexString(messageDigest.digest());

        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
            e.printStackTrace();
        }

        return cacheKey;
    }

    /**
     * 计算为 十六进制的 字符串
     *
     * @param digest
     * @return
     */
    private String bytestoHexString(byte[] digest) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < digest.length; i++) {
            String hex = Integer.toHexString(0xFF & digest[i]);
            if (hex.length() == 1) {
                sb.append('0');
            } else {
                sb.append(hex);
            }
        }
        return sb.toString();
    }

    /**
     * 计算位图的 采样比例
     *
     * @param options
     * @param reqWidth  :需要显示的宽
     * @param reqHeight
     * @return
     */
    public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

        //获取位图的原大小
        int w = options.outWidth;
        int h = options.outHeight;

        int inSampleSize = 1;

        if (w > reqWidth || h > reqHeight) {
            if (w > h) {
                inSampleSize = Math.round((float) h / (float) reqHeight);
            } else {
                inSampleSize = Math.round((float) w / (float) reqWidth);
            }
        }

        return inSampleSize;
    }


    /**
     * 位图重新采样  生成缩放比例后的图片
     * <p/>
     * bytes :字节
     *
     * @param reqWidth        :要显示的宽
     * @param reqHeight:要显示的高
     * @return
     */
    public Bitmap decodeSampleBitmapFromBytesStream(byte[] bytes, int reqWidth, int reqHeight) {

        BitmapFactory.Options options = new BitmapFactory.Options();

        //只解析边界,不占用内存
        options.inJustDecodeBounds = true;

        //得到边界的 宽 和 高 ,并存入 options中
        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);

        //得到缩放 值
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        options.inJustDecodeBounds = false;// false :图片加载到内存中去

        //再解码,这时图片就加载到内存中了,这时的图片是 缩放比例后的图片
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    }


    //添加到内存缓存
    public void addBitmapToCache(String url, Bitmap bitmap) {

        //将 url 生成 十六进制的字符串作为 key,方便操作
        String key = hashkeyForDisk(url);

        if (getBitmapFromCache(key) == null) {
            lruCache.put(key, bitmap);
        }
    }

    //从内存缓存中 读取对象
    public Bitmap getBitmapFromCache(String url) {
        String key = hashkeyForDisk(url);
        System.out.print("从内存中取图片");
        return lruCache.get(key);
    }


    /**
     * 下载图片并 缓存,磁盘 内存中
     *
     * @param url
     * @param callback
     */
    public void putCache(final String url, final Callback callback) {


        /**
         * 异步加载
         * 参数:
         *  string :url
         *  void : 进度值,这里为 void 不需要返回了
         *  Bitmap: 返回值
         *
         */
        new AsyncTask<String, Void, Bitmap>() {
            @Override
            protected Bitmap doInBackground(String... params) {

                //计算 md5 的摘要转化为十六进制的字符串,做为存入的键 ,相同的字符串,计算出来是一样
                String key = hashkeyForDisk(params[0]);   //取第一个参数

                DiskLruCache.Editor editor = null;//磁盘添加缓存的时侯必须得到一个编辑器
                try {
                    URL uri = new URL(params[0]);
                    HttpURLConnection conn = (HttpURLConnection) uri.openConnection();

                    //连接设置
                    conn.setReadTimeout(1000 * 30);
                    conn.setConnectTimeout(1000 * 30);

                    //下载的内容存入字节流中
                    ByteArrayOutputStream baos = null;
                    if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {

                        BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());

                        baos = new ByteArrayOutputStream();

                        byte[] bytes = new byte[1024];
                        int len = -1;
                        while ((len = bis.read(bytes)) != -1) {
                            baos.write(bytes, 0, len);
                        }
                        bis.close();
                        baos.close();
                        conn.disconnect();
                    }

                    if (baos != null) {
                        Bitmap bitmap = decodeSampleBitmapFromBytesStream(baos.toByteArray(), 100, 100);

                        //图片采样 存入 内存缓存
                        addBitmapToCache(params[0], bitmap);

                        //图片采样 存入 磁盘缓存
                        editor = diskLruCache.edit(key);

                        //位图压缩后输出 参数:压缩格式 ,质量(100:不压缩,30:压缩70%,输出流)
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, editor.newOutputStream(0));
                        editor.commit();
                    }

                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    try {
                        editor.abort();//放弃写入
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    e.printStackTrace();
                }

                System.out.print("从网络中下载图片");
                return bitmap;
            }

            /**
             * 下载完后
             * @param bitmap
             */
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                callback.response(bitmap);

            }
        }.execute(url);
    }

    /**
     * 根据key 获取 磁盘缓存
     *
     * @param url
     * @return
     */
    public InputStream getDiskCache(String url) {
        String key = hashkeyForDisk(url);
        try {

            //  获取 磁盘缓存快照对象
            DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
            if (snapshot != null) {
                System.out.print("从磁盘中取土图片");

                return snapshot.getInputStream(0);//拿 第几个图片,因为存的时侯是 1
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 磁盘缓存关闭
     */
    public void close() {
        if (diskLruCache != null && !diskLruCache.isClosed()) {
            try {
                diskLruCache.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 刷新磁盘缓存 操作
     */
    public void flush() {
        if (diskLruCache != null) {
            try {
                diskLruCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 定义 回调接口 下载完后,将图片传进来
     *
     * @param <T>
     */
    public interface Callback<T> {
        public void response(T entity);
    }

}

//网络图片下载 实现双缓存
public class ImgCache extends AppCompatActivity {

    private ImageView imageView;

    private LruCacheUtils lruCacheUtils;

    private static final String DISK_CACHE_SUBDIR = "temp";
    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10;//10MB


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_img_cache);
        imageView = (ImageView) findViewById(R.id.imageView3);
    }


    /**
     * 显示图片
     *
     * @param view
     */
    public void show(View view) {

        //String url = "http://p0.so.qhimg.com/t01472684773000a3bf.jpg";

        String url = "http://p4.so.qhimg.com/t01a8916cb0cddcb679.jpg";
        loadBitmap(url, imageView);
    }

    private void loadBitmap(String url, final ImageView imageView) {

        //从内存 缓存中取图片
        Bitmap bitmap = lruCacheUtils.getBitmapFromCache(url);
        if (bitmap == null) {

            //从磁盘 缓存中取图片
            InputStream in = lruCacheUtils.getDiskCache(url);

            if (in == null) {
                //下载图片
                lruCacheUtils.putCache(url, new LruCacheUtils.Callback<Bitmap>() {
                    @Override
                    public void response(Bitmap entity) {
                        imageView.setImageBitmap(entity);
                    }
                });
            } else {
                //从磁盘中取出图片
                bitmap = BitmapFactory.decodeStream(in);
                //图片 添加到内存 缓存中
                lruCacheUtils.addBitmapToCache(url, bitmap);
                imageView.setImageBitmap(bitmap);
            }
        } else {
            imageView.setImageBitmap(bitmap);
        }
    }


    @Override
    protected void onResume() {
        super.onResume();
        /**
         * activity 激活时打开缓存
         */
        lruCacheUtils = LruCacheUtils.getInstance();
        lruCacheUtils.open(this, DISK_CACHE_SUBDIR, DISK_CACHE_SIZE);

    }

    @Override
    protected void onPause() {
        super.onPause();
        lruCacheUtils.close();
    }

    @Override
    protected void onStop() {
        super.onStop();
        lruCacheUtils.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值