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

图片三级缓存

程序员文章站 2022-07-02 09:45:54
...

为什么要使用三级缓存

如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了
假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响
特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知
所以提出三级缓存策略,通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量

什么是三级缓存

网络缓存, 不优先加载, 速度慢,浪费流量
本地缓存, 次优先加载, 速度快
内存缓存, 优先加载, 速度最快

三级缓存原理

首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中
之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片
总之,只在初次访问新内容时,才通过网络获取图片资源

代码实现

  1. 新建一个BitmapUtils
 package cn.iaapp.app.sixman.util;

import android.graphics.Bitmap;
import android.widget.ImageView;

import com.youth.banner.Banner;

import java.util.ArrayList;
import java.util.List;

/**
 * 用来做三级缓存
 * Created by 梁 on 2018/6/1.
 */

public class BitmapUtils {

    private  NetCacheUtils netCacheUtils  ;  //网络下载
    private LocalUtils localUtils ;   //本地下载
    private final MemoryCacheUtils memoryCacheUtils;


    private  List<Bitmap> bitmapList = new ArrayList<>() ; //用于banner列表

    public BitmapUtils() {
        memoryCacheUtils = new MemoryCacheUtils();
        localUtils = new LocalUtils(memoryCacheUtils) ;
         netCacheUtils = new NetCacheUtils(localUtils,memoryCacheUtils);

    }

    /**
     * 用来三级缓存显示图片
     * @param imageView
     * @param url
     */
    public void  disPlay(ImageView imageView , String url)
    {

        //   1. 内存缓存 速度很快不浪费流量   优先
        Bitmap bitmapMemory = memoryCacheUtils.getMemoryCache(url);
        if (bitmapMemory != null)
        {
            imageView.setImageBitmap(bitmapMemory);
            return ;
        }
//        2.    本地缓存  速度快 不浪费流量    其次
        Bitmap bitmap = localUtils.getLocalCache(url);
        if (bitmap != null)
        {
            imageView.setImageBitmap(bitmap);
            return ;
        }
//        3.    网络缓存  速度慢浪费流量      最后
        netCacheUtils.getBitmapFromNet(imageView,url);

    }


    public void  disPlayBanner(Banner banner , List<String> urlList){

        //   1. 内存缓存 速度很快不浪费流量   优先

//        for (int i = 0 ; i < urlList.size() ; i ++)
//        {
//            Bitmap bitmap = memoryCacheUtils.getMemoryCache(urlList.get(i));
//            if (bitmap != null)
//            bitmapList.add(bitmap) ;
//        }
//        if (bitmapList .size() != 0)
//        {
//            banner.setImages(bitmapList);
//            return ;
//        }
//
////        2.  本地缓存  速度快 不浪费流量    其次
//        for (int i = 0 ; i < urlList.size() ; i ++)
//        {
//            Bitmap bitmap = localUtils.getLocalCache(urlList.get(i));
//            if (bitmap != null)
//            bitmapList.add(bitmap) ;
//        }
//        if (bitmapList .size() != 0)
//        {
            banner.setImages(bitmapList);
//            return ;
//        }

//        3.    网络缓存  速度慢浪费流量      最后
        netCacheUtils.getBitmapListFromNet(banner , urlList);
    }





}
  1. 网络缓存
package cn.iaapp.app.sixman.util;

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

import com.youth.banner.Banner;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 网络缓存工具类
 * Created by 梁 on 2018/6/1.
 */

public class NetCacheUtils {

    private List <Bitmap> bitmapList = new ArrayList<>();

    private LocalUtils localUtils ;
    private MemoryCacheUtils memoryCacheUtils ;
    public NetCacheUtils(LocalUtils localUtils, MemoryCacheUtils memoryCacheUtils) {

        this.localUtils = localUtils ;
        this.memoryCacheUtils = memoryCacheUtils ;
    }

    public   void getBitmapFromNet(ImageView imageView , String url){
        //异步下载图片
        new BitmapTask().execute(imageView,url);
    }


    // 获取bitmap列表
    public void getBitmapListFromNet(Banner banner, final List<String> urlList) {


        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0 ; i < urlList.size() ; i ++)
                {

                    Bitmap bitmap = download(urlList.get(i));

                    //写本地缓存 和内存缓存
                    localUtils.setLocalCache(bitmap,urlList.get(i));
                    memoryCacheUtils.setMemoryCache(urlList.get(i),bitmap);
                    bitmapList.add(bitmap) ;
                }

            }
        }).start();

        banner.setImages(bitmapList) ;

    }













    class BitmapTask extends AsyncTask<Object,Void ,Bitmap>
    {


        private ImageView imageView;
        private String url;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Bitmap doInBackground(Object... objects) {
            imageView = (ImageView) objects[0];
            url = (String) objects[1];
            imageView.setTag(url);  //与url 绑定一起
            //使用url 下载图片
            Bitmap bitmap = download(url);
            return bitmap;
        }


        @Override
        protected void onPostExecute(Bitmap result) {
            //给imagview  设置图片
            //由于Listview 的重用机制,导致某个item有可能展示重用的那个item的图片导致图片凑乱
            // 解决方案 确保当前设置的图片和当前显示的imageview 完全匹配

            String url  = (String) imageView.getTag();
            if (this.url .equals(url))
            { //判断当前下载的图片的url 和imageurl 是否一致
                if (result != null)
                    imageView.setImageBitmap(result);
                //写本地缓存
                localUtils.setLocalCache(result,url);
                memoryCacheUtils.setMemoryCache(url,result);
            }

        }
    }


    //使用url 下载图片
    private Bitmap download(String url) {
        HttpURLConnection conn = null;
        try {
              conn  = (HttpURLConnection) new URL(url).openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.connect();

            int responseCode = conn.getResponseCode();
            if (responseCode == 200)
            {
                InputStream inputStream = conn.getInputStream();
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                return bitmap ;
            }


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

        return  null ;
    }

}
  1. 本地缓存
package cn.iaapp.app.sixman.util;

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

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.List;


/**
 * 本地缓存的工具类
 * Created by 梁 on 2018/6/1.
 */

public class LocalUtils {



    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/jft_Cache/" ;   //缓存文件夹


    private  MemoryCacheUtils memoryCacheUtils ;
    public LocalUtils(MemoryCacheUtils memoryCacheUtils) {
        this.memoryCacheUtils = memoryCacheUtils ;
    }

    /**
     * 写缓存
     * @param bitmap  从网络下载得到的bitmap
     * @param url  图片的url 作为图片的唯一标识
     */
    public void  setLocalCache(Bitmap bitmap ,String url)     {
        //将图片保存到本地文件

        File dir = new File(path) ;
        if(!dir.exists() || !dir.isDirectory())
        {
            dir.mkdirs() ; // 创建文件夹
        }

        try {
            File cacheFile = new File(dir,MD5Util.md5String(url)+".JPEG") ;
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(cacheFile)) ;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }



    //读缓存
    public Bitmap  getLocalCache(String url){

        File cacheFile = new File(path,MD5Util.md5String(url) + ".JPEG");

        if (cacheFile.exists())
        {
             // 缓存路径
            try {
                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(cacheFile));
                //写内存缓存
                memoryCacheUtils.setMemoryCache(url,bitmap);
                return bitmap ;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }


        return null ;
    }



}

4.内存缓存

package cn.iaapp.app.sixman.util;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

import java.lang.ref.SoftReference;
import java.util.HashMap;

/**
 * 内存缓存
 * Created by 梁 on 2018/6/1.
 */

public class MemoryCacheUtils {

    private HashMap<String , SoftReference<Bitmap>> mHashMap = new HashMap<>() ;

    private LruCache<String , Bitmap>  mLruCache;

    public MemoryCacheUtils( ) {

        //获取虚拟机分配的最大内存
        long maxMemory = Runtime.getRuntime().maxMemory();
        //maxSize :内存上限
        mLruCache = new LruCache<String,Bitmap>((int) (maxMemory / 8))
        {
            @Override // 用来返回单个对象占用内存的大小
            protected int sizeOf(String key, Bitmap value) {
                //计算图片占用内存的大小       //   int byteCount = value.getByteCount();
                int byteCount = value.getRowBytes() * value.getHeight();
                return byteCount;
            }
        }; //一般给 2M 也就是 1/8
    }

    //写缓存
    public  void setMemoryCache(String url , Bitmap bitmap){

//        SoftReference<Bitmap> bitmapSoftReference = new SoftReference<Bitmap>(bitmap); //用软引用包装
//        mHashMap.put(url,bitmapSoftReference) ;

        mLruCache.put(url,bitmap) ;
    }

    //读缓存
    public  Bitmap getMemoryCache(String url){

//        SoftReference<Bitmap> soft = mHashMap.get(url);
//        if (soft != null)
//        {
//            Bitmap bitmap = soft.get();  //从软引用中取出当前对象
//            return  bitmap;
//        }
        return mLruCache.get(url) ;

    }

}

使用

   BitmapUtils bitmapUtils = new BitmapUtils();
   bitmapUtils.disPlay(mIamgeView,url);

Glide 的内存缓存

Glide 是默认开启了内存缓存的,只要你通过 Glide 加载一张图片,他就会缓存到内存中,只要他还没被从内存中清理之前,下次使用 Glide 都会从内存缓存中加载。大大提升了图片加载的效率。

当然如果你有特殊要求,可以添加一行代码把默认开启的内存缓存关闭掉。
?
1
2
3
4

Glide.with(this)
.load(url)
.skipMemoryCache(true)//关闭内存缓存
.into(imageView);

Glide 的内存缓存实际上和我们上面说的差别不大,使用的也是LruCache算法,不过他还结合了一种弱引用机制,共同完成了内存缓存功能。

Glide 的硬盘缓存

关于 Glide 硬盘缓存使用也是十分简单。

       Glide.with(this)
                .load(url)
                .diskCacheStrategy(DiskCacheStrategy.RESULT)
                .into(imageView);

一个 diskCacheStrategy( ) 方法就可以调整他的硬盘缓存策略。其中可以传入的参数有四种:

 DiskCacheStrategy.NONE: 表示不缓存任何内容。
    DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
    DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
    DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。

Glide 的硬盘缓存是默认将图片压缩转换后再缓存到硬盘中,这种处理方式再避免OOM的时候会经常看见。

如果需要改变硬盘缓存策略只需要改变其传入的参数即可。