Android图片缓存架构

Android图片缓存架构

标签(空格分隔): Android


Android图片三级缓存机制

  • 一级缓存:强引用缓存(内存) 内存溢出,都不会回收
  • 二级缓存:弱引用缓存(内存) 内存不足的时候回收
  • 三级缓存:本地缓存(硬盘)写入内部存储Sdcard
下载地址 : ImageLoaderDemo项目地址

一、缓存机制

一级缓存

采用的是LinkedHashMap,主要原因是LinkedHashMap内部封装的数据结构算法包含LRU(最少使用算法)算法,最少使用算法通过操作这个数据的次数来判断的

二级缓存

使用的是ConcurrentHashMap,主要原因是同步的

三级缓存

直接存储到硬盘中

二、缓存图片辅助类

1.图片下载网络请求,使用HttpURLConnection

/**
 * Author: river
 * Date: 2015/12/29 10:33
 * Description:
 */
public class HttpUtils {
    /**
     * 下载
     *
     * @param key
     * @return
     * @throws IOException
     */
    public static InputStream download(String key) throws IOException {

        HttpURLConnection conn = (HttpURLConnection) new URL(key).openConnection();

        return conn.getInputStream();

    }
}

2.图片下载后,缓存名称转换MD5,主要是MD5具有抗修改性

/**
 * Author: river
 * Date: 2015/12/29 13:58
 * Description: 
 */
public class MD5Utils {

    /**
     * MD5加密
     *
     * @param key
     * @return
     */
    public static String decode(String key) {

        MessageDigest messageDigest = null;

        try {
            messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.reset();

            //utf-8编码
            messageDigest.update(key.getBytes("utf-8"));

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        byte[] byteArray = messageDigest.digest();
        StringBuffer buffer = new StringBuffer();

        for (int i = 0; i < byteArray.length; i++) {

            if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) {
                buffer.append("0").append(Integer.toHexString(0xFF & byteArray[i]));
            } else {
                buffer.append(Integer.toHexString(0xFF & byteArray[i]));
            }
        }

        return buffer.toString();

    }
}

三、缓存图片框架

package com.cn.image;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.widget.ImageView;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * // 一级缓存:强引用缓存(内存) 内存溢出,都不会回收   20张
 * // 二级缓存:弱引用缓存(内存) 内存不足的时候回收      超过20张
 * // 三级缓存:本地缓存(硬盘)写入内部存储Sdcard
 * <p/>
 * Author: river
 * Date: 2015/12/28 20:17
 * Description:
 */
public class ImageLoader {
    /**
     * 一级缓存最大数量
     */
    public static final int MAX_CAPACITY = 20;
    /**
     * 一级缓存
     * <p/>
     * key:图片地址
     * value:图片
     * <p/>
     * MAX_CAPACITY:存储最大数量
     * 0.75f经验值
     * accessOrder:true访问排序;false插入排序
     * <p/>
     * LinkedHashMap:内部包含LRU近期最少使用算法
     */
    private final LinkedHashMap<String, Bitmap> firstCacheMap = new LinkedHashMap<String, Bitmap>(MAX_CAPACITY, 0.75f, true) {

        //根据返回值,移除map中最老的值
        @Override
        protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) {
            //超过MAX_CAPACITY这个数量,则存储到其他地方
            if (this.size() > MAX_CAPACITY) {
                //加入二级缓存
                secondCacheMap.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));

                //加入本地缓存
                addDiskCache(eldest.getKey(), eldest.getValue());

                return true; //移除超出的缓存
            }

            return super.removeEldestEntry(eldest); //false
        }
    };

    /**
     * 二级缓存
     * <p/>
     * ConcurrentHashMap:线程安全的,为了并发的安全
     */
    private ConcurrentHashMap<String, SoftReference<Bitmap>> secondCacheMap = new ConcurrentHashMap<>();

    private Context context;

    private static ImageLoader mImageLoader;

    private ImageLoader(Context context) {
        this.context = context;
    }


    public static ImageLoader getInstance(Context context) {
        if (mImageLoader == null) {
            synchronized (ImageLoader.class) {
                if (mImageLoader == null) {
                    mImageLoader = new ImageLoader(context);
                }
            }
        }

        return mImageLoader;
    }


    /**
     * 添加到本地缓存
     *
     * @param key   图片的路径(会被当做图片名称保存到硬盘上)
     * @param value 图片
     */
    private void addDiskCache(String key, Bitmap value) {
        //http://7xlovk.com2.z0.glb.qiniucdn.com/upload/com/%E9%A1%B9%E7%9B%AE/%E4%B8%8A%E6%B5%B717%E8%8B%B1%E9%87%8C/Sjt_04.jpg
        //消息摘要算法 MD5算法 抗修改性
        String fileName = MD5Utils.decode(key);

        //存储路径
        String path = context.getCacheDir().getAbsolutePath() + File.separator + fileName;

        FileOutputStream os = null;
        try {
            os = new FileOutputStream(path);
            value.compress(Bitmap.CompressFormat.JPEG, 100, os);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {

            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }


    /**
     * 加载图片
     *
     * @param url
     * @param imageView
     */
    public void loadImage(String url, ImageView imageView) {
        // 读取缓存
        Bitmap bitmap = getFromCache(url);

        if (bitmap != null) {
            // 取消该图片对应的所有异步请求
            cancelDownload(url, imageView);

            // 设置图片
            imageView.setImageBitmap(bitmap);

        } else {
            // 设置加载过程中,空白图片
            imageView.setImageDrawable(defaultImage);

            // 访问网络,请求图片
            AsynImageLoadTask task = new AsynImageLoadTask(imageView);
            task.execute(url);
        }

    }

    /**
     * 标记异步线程下载
     * <p/>
     * 可能有多个异步程序在下载同一张图片
     *
     * @param url
     * @param imageView
     */
    private void cancelDownload(String url, ImageView imageView) {
        AsynImageLoadTask task = new AsynImageLoadTask(imageView);

        String downloadKey = task.key;

        if (downloadKey == null || !downloadKey.equals(url)) {
            //设置标示
            task.cancel(true);
        }

    }

    /**
     * 获取缓存
     *
     * @param key
     * @return
     */
    private Bitmap getFromCache(String key) {
        Bitmap bitmap = null;

        //从一级缓存加载
        synchronized (firstCacheMap) {
            bitmap = firstCacheMap.get(key);

            //保持图片的fresh新鲜(LRU)
            if (bitmap != null) {
                firstCacheMap.remove(key);
                firstCacheMap.put(key, bitmap);
                return bitmap;
            }
        }

        //从二级缓存加载
        SoftReference<Bitmap> softReference = secondCacheMap.get(key);

        //有可能被回收
        if (softReference != null) {
            bitmap = softReference.get();

            if (bitmap != null) {
                // 添加到一级缓存,为了下一次读取更快
                firstCacheMap.put(key, bitmap);

                return bitmap;
            }

        } else {
            //软引用被回收了,清除缓存
            secondCacheMap.remove(key);
        }

        //从三级本地缓存加载
        bitmap = getFromLocal(key);

        if (bitmap != null) {
            // 添加到一级缓存,为了下一次读取更快
            firstCacheMap.put(key, bitmap);

            return bitmap;
        }

        return null;
    }

    /**
     * 读取本地缓存
     *
     * @param key
     * @return
     */
    private Bitmap getFromLocal(String key) {
        String fileName = MD5Utils.decode(key);

//        if (fileName == null) {
//            return null;
//        }

        //存储路径
        String path = context.getCacheDir().getAbsolutePath() + File.separator + fileName;

        FileInputStream is = null;
        try {
            File file = new File(path);

            if (file.exists()) {
                //读取文件
                is = new FileInputStream(file);
                return BitmapFactory.decodeStream(is);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {

            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

        return null;
    }

    /**
     * 异步线程加载网络图片
     */
    public class AsynImageLoadTask extends AsyncTask<String, Void, Bitmap> {
        /**
         * 图片的地址
         */
        private String key;
        private ImageView imageView;

        public AsynImageLoadTask(ImageView imageView) {
            this.imageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            key = params[0];

            //下载图片
            return download(key);
        }


        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);

            //取消下载
            if (isCancelled()) {
                bitmap = null;
            }

            if (bitmap != null) {
                //下载完成后,添加到一级缓存中
                addFirstCache(key, bitmap);

                //显示图片
                imageView.setImageBitmap(bitmap);
            }
        }

    }

    /**
     * 默认图片
     */
    private DefaultImage defaultImage = new DefaultImage();

    /**
     * 默认图片
     */
    public class DefaultImage extends ColorDrawable {
        public DefaultImage() {
            super(Color.GRAY);
        }
    }

    /**
     * 添加到一级缓存
     *
     * @param key
     * @param bitmap
     */
    private void addFirstCache(String key, Bitmap bitmap) {
        if (bitmap != null) {
            //firstCacheMap线程不同步,所以添加同步锁
            synchronized (firstCacheMap) {
                firstCacheMap.put(key, bitmap);
            }
        }
    }

    /**
     * 下载网络图片
     *
     * @param key
     * @return
     */
    private Bitmap download(String key) {
        InputStream is = null;

        try {
            is = HttpUtils.download(key);

            return BitmapFactory.decodeStream(is);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}

用法:

ImageLoader.getInstance(context).loadImage(list.get(position), imageview);

权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值