Android图片的三级缓存

图片的三级缓存目的

主要目的是为了节省流量、加快加载速度;

每个 app 都会有大量的网络图片存在,当我们不做处理,每次打开 app 都去加载大量网络图片时,会耗费大量的流量,当网速不好时加载速度很慢;

三级缓存介绍以及优点

三级缓存即:内存缓存、本地缓存和网络缓存

内存缓存:加载速度快、不耗费流量、优先加载

本地缓存:加载速度快、不耗费流量、次级加载

网络缓存:加载速度慢、耗费流量、最后加载

三级缓存原理

首次打开 app 中的图片时,通过网络加载图片并将图片保存到本地和内存中。

再次打开 app 时,先从内存缓存中取出图片;如果没有,再从本地取出图片;如果还是没有说明本地图片被清理或者图片为新资源,采用网络加载图片。

下面是工具类代码和具体使用的 GitHub 下载地址:https://github.com/wuqingsen/PhotoCache

CSDN 下载地址:https://download.csdn.net/download/wuqingsen1/11082536

创建工具类

图片缓存工具类:BitmapUtils ;

使用方法,第一个参数传 ImageView 控件;第二个参数传 String 类型图片链接:

BitmapUtils.getInstance().disPlay(imageView, url);
import android.graphics.Bitmap;
import android.util.Log;
import android.widget.ImageView;

import com.example.qd.photocache.R;

/**
 * author: wu
 * date: on 2019/4/2.
 * describe:自定义的BitmapUtils,实现三级缓存
 */
public class BitmapUtils {

    private NetCacheUtils mNetCacheUtils;
    private LocalCacheUtils mLocalCacheUtils;
    private MemoryCacheUtils mMemoryCacheUtils;
    private static BitmapUtils bitmapUtils;

    public static BitmapUtils getInstance() {
        if (bitmapUtils == null) {
            bitmapUtils = new BitmapUtils();
        }
        return bitmapUtils;
    }

    public BitmapUtils() {
        mMemoryCacheUtils = new MemoryCacheUtils();
        mLocalCacheUtils = new LocalCacheUtils();
        mNetCacheUtils = new NetCacheUtils(mLocalCacheUtils, mMemoryCacheUtils);
    }

    public void disPlay(ImageView ivPic, String url) {
        ivPic.setImageResource(R.mipmap.ic_launcher);
        Bitmap bitmap;
        //内存缓存
        bitmap = mMemoryCacheUtils.getBitmapFromMemory(url);
        if (bitmap != null) {
            ivPic.setImageBitmap(bitmap);
            Log.e("=====", "从内存获取图片");
            return;
        }

        //本地缓存
        bitmap = mLocalCacheUtils.getBitmapFromLocal(url);
        if (bitmap != null) {
            ivPic.setImageBitmap(bitmap);
            Log.e("=====", "从本地获取图片");
            //从本地获取图片后,保存至内存中
            mMemoryCacheUtils.setBitmapToMemory(url, bitmap);
            return;
        }
        //网络缓存
        mNetCacheUtils.getBitmapFromNet(ivPic, url);
    }
}

内存缓存工具类:MemoryCacheUtils


/**
 * author: wu
 * date: on 2019/4/2.
 * describe:内存缓存
 */
public class MemoryCacheUtils {

    private LruCache<String,Bitmap> mMemoryCache;

    public MemoryCacheUtils(){
        long maxMemory = Runtime.getRuntime().maxMemory()/8; 
        mMemoryCache=new LruCache<String,Bitmap>((int) maxMemory){
            //用于计算每个条目的大小
            @Override
            protected int sizeOf(String key, Bitmap value) {
                int byteCount = value.getByteCount();
                return byteCount;
            }
        };

    }

    /**
     * 从内存中读图片
     * @param url
     */
    public Bitmap getBitmapFromMemory(String url) { 
        Bitmap bitmap = mMemoryCache.get(url);
        return bitmap;

    }

    /**
     * 往内存中写图片
     * @param url
     * @param bitmap
     */
    public void setBitmapToMemory(String url, Bitmap bitmap) { 
        mMemoryCache.put(url,bitmap);
    }
}

本地缓存工具类 LocalCacheUtils :

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * author: wu
 * date: on 2019/4/2.
 * describe:本地缓存
 */
public class LocalCacheUtils {

    private static final String CACHE_PATH= Environment.getExternalStorageDirectory().getAbsolutePath()+"/WerbNews";

    /**
     * 从本地读取图片
     * @param url
     */
    public Bitmap getBitmapFromLocal(String url){
        String fileName = null;//把图片的url当做文件名,并进行MD5加密
        try {
            fileName = MD5Encoder.encode(url);
            File file=new File(CACHE_PATH,fileName);

            Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));

            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 从网络获取图片后,保存至本地缓存
     * @param url
     * @param bitmap
     */
    public void setBitmapToLocal(String url,Bitmap bitmap){
        try {
            String fileName = MD5Encoder.encode(url);//把图片的url当做文件名,并进行MD5加密
            File file=new File(CACHE_PATH,fileName);

            //通过得到文件的父文件,判断父文件是否存在
            File parentFile = file.getParentFile();
            if (!parentFile.exists()){
                parentFile.mkdirs();
            }

            //把图片保存至本地
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

网络缓存工具类 NetCacheUtils :

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * author: wu
 * date: on 2019/4/2.
 * describe:网络缓存
 */
public class NetCacheUtils {
    private LocalCacheUtils mLocalCacheUtils;
    private MemoryCacheUtils mMemoryCacheUtils;

    public NetCacheUtils(LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) {
        mLocalCacheUtils = localCacheUtils;
        mMemoryCacheUtils = memoryCacheUtils;
    }

    /**
     * 从网络下载图片
     * @param ivPic 显示图片的imageview
     * @param url   下载图片的网络地址
     */
    public void getBitmapFromNet(ImageView ivPic, String url) {
        new BitmapTask().execute(ivPic, url);//启动AsyncTask

    }

    /**
     * AsyncTask就是对handler和线程池的封装
     * 第一个泛型:参数类型
     * 第二个泛型:更新进度的泛型
     * 第三个泛型:onPostExecute的返回结果
     */
    class BitmapTask extends AsyncTask<Object, Void, Bitmap> {

        private ImageView ivPic;
        private String url;

        /**
         * 后台耗时操作,存在于子线程中
         * @param params
         * @return
         */
        @Override
        protected Bitmap doInBackground(Object[] params) {
            ivPic = (ImageView) params[0];
            url = (String) params[1];

            return downLoadBitmap(url);
        }

        /**
         * 更新进度,在主线程中
         * @param values
         */
        @Override
        protected void onProgressUpdate(Void[] values) {
            super.onProgressUpdate(values);
        }

        /**
         * 耗时方法结束后执行该方法,主线程中
         * @param result
         */
        @Override
        protected void onPostExecute(Bitmap result) {
            if (result != null) {
                ivPic.setImageBitmap(result);
                System.out.println("从网络缓存图片啦.....");

                //从网络获取图片后,保存至本地缓存
                mLocalCacheUtils.setBitmapToLocal(url, result);
                //保存至内存中
                mMemoryCacheUtils.setBitmapToMemory(url, result);

            }
        }
    }

    /**
     * 网络下载图片
     * @param url
     * @return
     */
    private Bitmap downLoadBitmap(String url) {
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(url).openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.setRequestMethod("GET");

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {
                //图片压缩
                BitmapFactory.Options options = new BitmapFactory.Options();
//                options.inSampleSize=2;//宽高压缩为原来的1/2
                options.inPreferredConfig=Bitmap.Config.ARGB_4444;
                Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream(),null,options);
                return bitmap;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            conn.disconnect();
        }

        return null;
    }
}

上面用到了 MD5 加密,MD5 加密工具类:

import java.security.MessageDigest;

/**
 * author: wu
 * date: on 2019/4/2.
 * describe:MD5加密
 */
public class MD5Encoder {
    /**
     * Md5Encoder
     * @param string
     */
    public static String encode(String string) throws Exception {
        byte[] hash = MessageDigest.getInstance("MD5").digest(
                string.getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
    }
}

演示代码

工具类添加完毕,演示一下三级缓存的具体使用;

新建 adapter ,在里面的每个 item 都去加载图片;列表长度为10,前五个加载同一张图片,后五个加载另一张图片,在 adapter 的 onBindViewHolder 中添加代码如下:

        Log.e("=====position", position + "");
        if (position > 5) {
            BitmapUtils.getInstance().disPlay(holder.iv_icon, "http://static.gamemm.com/upload/avatar/201903/29/62018_1553847955.jpg");
        } else {
            BitmapUtils.getInstance().disPlay(holder.iv_icon, "http://static.gamemm.com/upload/record/201904/2/62511_1554183931_95789.jpg");
        }

下面是第一次打开 app 打印日志,可以看出:

首次加载没有打印加载方式,默认是从网络加载图片;当图片加载完成后,因为图片链接一样,所以直接从内存中取出图片;

下面是关闭进程再次打开 app 的打印日志,可以看出:

进程被销毁,内存中图片也就没有了;所以从本地获取图片并存储到内存中;

这是源码 GitHub 下载地址:https://github.com/wuqingsen/PhotoCache

猜你喜欢

转载自blog.csdn.net/wuqingsen1/article/details/88972505