Android 图片三级缓存原理实现

图片三级缓存类原理:

  1. 先从缓存中查找,缓冲中没有,再从SD卡中查找,SD卡没有,从网络查找下载,下载完成,在SD卡和缓存中分别保存一份
  2. Lruch : 类似于内存强引用的缓存,存储方式为键值对存值(K,V),取值 方便,当缓存中内存的大小超出本身的内存的时候,它会自动将最前面的内存强制抛出交给垃圾回收机制(GC)进行回收
  3. set集合在安卓中的使用:去重的功能
  4. 如何避免图片错位的问题,给set集合中的imagview添加唯一 的 Tag值, 图片下载完成之后,再将图片值给imageview 的时候遍历整个set进行进行对比,符合的再设置
  5. 一般使用的话,结合application使用

直接贴代码把,懒的写,我要说一下为什么写这个,那是因为有时候写个小demo,从网上依赖图片框架太麻烦,索性直接写个工具类完事

MainActivity.java

    String url = "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3483712804,2789478993&fm=27&gp=0.jpg";
    private ImageView mImg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImg = findViewById(R.id.img);

    }

    public void load(View view) {
        initLoad();
    }

    private void initLoad() {
        ImageLoaderUtils utils = new ImageLoaderUtils();
        utils.Load(url, mImg, this);
    }

ImageLoaderUtils.java

**
 * Created by ${zk} on 2018/4/18 0018.
 * 欢迎每一天
 * 图片的三级缓存类
 */
public class ImageLoaderUtils {
//        private Context mContext;
    private final static int MAX_POOLS = 5;//声明一个最大的开启线程数
    private ExecutorService thread_pool;//声明一个线程池
    private FileUtils       mFileUtils;//图片保存和读取工具类
    private Set<ImageView> imgs     = new HashSet<>();//创建一个存储图片控件的Set集合
    private int            max_size = (int) (Runtime.getRuntime().maxMemory()) / 1024 / 5;//规定每个线程最大内存
    //LruCache 类似于强引用缓存的,一旦超出最大值,会自动将前面的扔出缓存,便于垃圾回收机制回收
    //存储方式和Map相似,用的K,V存值的方式,便于图片查找
    private LruCache<String, Bitmap> lru = new LruCache<String, Bitmap>(max_size) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            return value.getByteCount() / 1024;

        }
    };


    /***
     * 三级判断
     * @param name
     * @param img
     */
    public void Load(String name, ImageView img, Context context) {
        Bitmap mBitmap;
//        mContext=context;
        //为空直接跳出
        if (name == null)
            return;
        if (img == null)
            return;
        //把name网址过滤
        String newFileName = name.replace("/", "").replace(".", "").replace(":", "")
                .replace("_", "").replace("=","");
        //img 添加tag
        img.setTag(newFileName);//为避免出现图片错位,将图片存储在其相对应的控件中,方便以后进行对比设置
        //imgs添加到Set集合
        imgs.add(img);
        //从lru中获取
        mBitmap = lru.get(newFileName);//K.V --->从lru中获取图片
        if (mBitmap != null) {
            img.setImageBitmap(mBitmap);
            return;
        }
        //从本地sd卡中获取图片
        if (mFileUtils == null) {
            mFileUtils = new FileUtils(context);
            mBitmap = mFileUtils.readBitmap(newFileName);
            if (mBitmap != null) {
                img.setImageBitmap(mBitmap);
                //将图片添加到lru中
                lru.put(newFileName, mBitmap);
                return;
            }
        }
        //从网络下载
        if (thread_pool == null) {
            thread_pool = Executors.newFixedThreadPool(MAX_POOLS);
        }
        //准备网络加载图片,传过去的是网址name
        thread_pool.execute(new ImgThread(name));

    }

    /**
     * 图片异步加载内部类
     */
    private class ImgThread implements Runnable {
        private String name;

        public ImgThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            //开始网络下载
            try {
                URL url = new URL(name);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                //设置联网的参数
                conn.setConnectTimeout(30 * 1000);
                conn.setReadTimeout(30 * 1000);
                conn.setDoInput(true);
//                conn.setDoOutput(true);  重要说明 这里是个梗,浪费我半天的时间,默认是false
                conn.setRequestMethod("GET");
                Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream());
                //把网址过滤
                String newFileName = name.replace("/", "").replace(".", "").replace(":", "")
                        .replace("_", "").replace("=","");
                //将下载下来的图片再次添加到SD卡和lru中
                if (bitmap != null) {
                    mFileUtils.saveBitmap(newFileName, bitmap);
                    lru.put(newFileName, bitmap);
                }
                Message msg = hand.obtainMessage();
                msg.what = 200;
                msg.obj = bitmap;
                Bundle bundle = new Bundle();
                bundle.putString("imgname", newFileName);
                msg.setData(bundle);
                hand.sendMessage(msg);

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

    @SuppressLint("HandlerLeak")
    Handler hand = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 200) {
                Bitmap bitmap = (Bitmap) msg.obj;
                String imgname = msg.getData().getString("imgname");
                //遍历Set集合,进行对比
                Iterator<ImageView> it = imgs.iterator();
                while (it.hasNext()) {
                    ImageView img = it.next();
                    String tag = (String) img.getTag();
                    //将子线程出来的图片名称和控件保存的相对应的图片名称进行比较,如果相同就设置
                    if (tag.equals(imgname)) {
                        img.setImageBitmap(bitmap);
                        return;
                    }
                }
            }
        }
    };
}

FileUtils.java

/**
 * Created by ${zk} on 2018/4/18 0018.
 * 欢迎每一天
 * 文件操作工具类,存储和读取图片
 */

public class FileUtils {
    private Context ctx;

    public FileUtils(Context ctx) {
        this.ctx = ctx;
    }

    /**
     * 判断SD卡是否存在的方法----》 存在返回 true ,不存在返回false
     * Environment.getExternalStorageState():获取sd卡的状态  Environment.MEDIA_MOUNTED:当前SD存在
     */
    private boolean isSdcard() {
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            return true;
        }

        return false;
    }

    /**
     * 获取文件目录的方法 /mnt/sdcard/android/data/catch/name;
     *
     * @return
     */
    private String getFile() {
        String str = null;
        str = ctx.getExternalCacheDir().getAbsolutePath();
        return str;
    }

    /**
     * 存储图片的方法
     */
    public void saveBitmap(String name, Bitmap bitmap) {
        //判断SDK是否存在,不存在直接return
        if (!isSdcard()) {
            return;
        }
        //获取图片完整存储路径:以图片网址为图片名称存储   /mnt/sdcard/android/data/catch/name
        String fileName = getFile() + "/" + name;
        Log.e("zk", "fileName++++" + fileName);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        //图片压缩
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
        File file = new File(fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            //写文件
            fos.write(baos.toByteArray());
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 读取图片的方法
     *
     * @param name 本地图片名字
     * @return 图片
     */
    public Bitmap readBitmap(String name) {
        Bitmap bitmap = null;
        if (!isSdcard())
            //没有sd卡
            return bitmap;
        //获取图片存储的完整路径
        String fileName = getFile() + "/" + name;
        bitmap = BitmapFactory.decodeFile(fileName);
        return bitmap;
    }

}

好了,conn.setDoOutput()方法,最好不要写,写了之后网上下载不出来图片,困惑了我半天的Bug

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值