欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

图片三级缓存

程序员文章站 2022-07-02 09:46:00
...

我们在开发中用了许多加载图片的框架都封装了缓存机制,图片的三级缓存的核心类就是LruCache类,尝试手写三级缓存工具类:

/**
 * 自定义的加载网络图片工具:三级缓存
 * @author wangk
 *
 */
public class MyBitmapUtils {

    private Context context;

    private LruCache<String, Bitmap> lruCache;

    private File rootFile;//本地缓存根目录

    private ExecutorService executorService;

    public MyBitmapUtils(Context context) {
        this.context = context;
        /**
         * maxSize:lruCache能管理的最大内存,此处设置为最大内存的1/8
         */
        int maxSize = (int) (Runtime.getRuntime().maxMemory()/8);//Runtime.getRuntime().maxMemory()当前应用可使用的最大内存
        lruCache = new LruCache<String, Bitmap>(maxSize){
            /**
             * 当前存储的对象,bitmap,如何计算大小
             */
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight();//每一行的字节个数*高度 =  bitmap的大小
            }
        };
        //本地缓存到data/data
        rootFile = context.getFilesDir();

        executorService = Executors.newFixedThreadPool(3);//创建固定大小的线程池,内部维护了3个线程
    }
    /**
     * 展示网络图片
     * @param image : 展示的空间
     * @param imageUrl: 图片的url
     */
    public void display(ImageView image, String imageUrl) {
        /**
         * 三级缓存  (图片)
         * 1.内存缓存 HashMap <key,value> key:图片url,value:bitmap
         *          v4:LruCache 类似hashmap <key,value>  lru算法:less recent use,最少最近使用
         * 2.本地缓存 保存sd卡,mnt/sdcard/zhbj_10/image/xxx.png
         *          xxx:图片名称,一般以图片的 url+md5 命名
         *             url:唯一,还能通过url获取本地缓存图片
         *             md5加密:为了避免url的特殊字符
         * 3.网络缓存
         *      
         * 使用步骤: 遵循原则:优先获取加载速度最快的缓存数据
         * 1.从内存中获取,如果获取到图片,直接展示。如果获取不到,从本地缓存获取
         * 2.从本地缓存中获取,如果获取到图片,先加载到内存,然后展示。如果获取不到,从网络获取
         * 3.从网络获取,如果获取到图片,加载到内存中,然后展示,保存到本地。
         */
        //1.从内存中获取图片
        Bitmap cacheBitmap = lruCache.get(imageUrl);
        if(cacheBitmap!=null){
            image.setImageBitmap(cacheBitmap);
            System.out.println("从内存中获取");
            return ;
        }
        //2. 从本地获取
        String imageName = MD5Encoder.encode(imageUrl);//url+md5加密
        File cacheFile = new File(rootFile, imageName);
        if(cacheFile.exists() && cacheFile.length()!=0){
            //加载到内存中,展示
            Bitmap decodeFileBitmap = BitmapFactory.decodeFile(cacheFile.getAbsolutePath());
            lruCache.put(imageUrl, decodeFileBitmap);
            image.setImageBitmap(decodeFileBitmap);
            System.out.println("从本地中获取");
            return;
        }
        //3.从网络获取
        int position = (Integer) image.getTag();
        executorService.execute(new DownLoadRunnable(image,imageUrl,position));
        System.out.println("从网络获取");

    }
    class DownLoadRunnable implements Runnable{
        private ImageView image;
        private String imageUrl;
        private int position;
        /**
         * 
         * @param image
         * @param imageUrl
         * @param position:请求的位置
         */
        public DownLoadRunnable(ImageView image, String imageUrl, int position) {
            this.image = image;
            this.imageUrl = imageUrl;
            this.position = position;
        }

        @Override
        public void run() {
            try {
                SystemClock.sleep(1000);
                //从网络获取:HttpUrlConnection获取网络图片
                URL url = new URL(imageUrl);
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                //设置超时时间
                httpURLConnection.setConnectTimeout(5*1000);
                //设置请求方式
                httpURLConnection.setRequestMethod("GET");
                //获取响应码
                int responseCode = httpURLConnection.getResponseCode();
                if(responseCode == 200){//请求成功
                    InputStream inputStream = httpURLConnection.getInputStream();
                    final Bitmap decodeStreamBitmap = BitmapFactory.decodeStream(inputStream);
                    //保存到内存
                    lruCache.put(imageUrl, decodeStreamBitmap);
                    //保存到本地(bitmap对象如何转换成本地文件)
                    /**
                     * compress:压缩
                     * format:压缩格式:png、jgp
                     * quality:压缩质量:0-100,0-完全压缩,100-不压缩
                     * outputStream:输出流,写到本地
                     */
                    File cacheFile = new File(rootFile, MD5Encoder.encode(imageUrl));
                    OutputStream ops = new FileOutputStream(cacheFile);
                    decodeStreamBitmap.compress(CompressFormat.PNG, 100, ops);
                    //展示
                    MainActivity mainActivity = (MainActivity) context;
                    mainActivity.runOnUiThread(new Runnable()//Runnable在子线程还是主线程中运行,主要看调用者
                    {

                        @Override
                        public void run() {
                            int currentPosition = (Integer) image.getTag();
                            //请求的位置如果是当前显示在屏幕上的位置
                            if(position == currentPosition){
                                image.setImageBitmap(decodeStreamBitmap);
                            }
                        }
                    });

                }

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

        }

    }

}