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

图片三级缓存

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

什么是三级缓存?

1). 图片的三级缓存

一级缓存: 内存缓存, 缓存的是bitmap对象, 用Map<String, Bitmap>结构保存, key是url

二级缓存: 本地(sd卡)缓存, 缓存的是图片文件, /storage/sdcard/Android/data/packageName/files/图片文件名(xxx.jpg)

三级缓存: 远程服务器缓存, 缓存的是图片文件, 远程服务器上的应用中

2). 如何使用三级缓存? -----如何根据图片的url动态显示图片?

String imagePath = http://p4.so.qhmsg.com/bdr/240/t0157596cfec8884249.jpg和ImageView对象

1). 根据url从一级缓存中取对应的bitmap对象

如果有, 显示(结束)

如果没有, 进入2)

2). 从二级缓存中查找: 得到文件名并在sd卡的缓存目录下加载对应的图片得到Bitmap对象

如果有: 显示, 缓存到一级缓存中(结束)

如果没有, 进入3)

3). 显示代表提示正在加载的图片, 启动分线程联网请求得到Bitmap对象

如果没有: 显示提示错误的图片(结束)

如果有:

显示

缓存到一级缓存

缓存到二级缓存

在ListView使用图片三级缓存会存在图片闪动的bug,如何解决

1). 原因

converView被复用了

2). 解决

a. 每次getView()都将图片的url保存到ImageView上: imageView.setTag(imagePath)

b. 在分线程准备请求服务器加载图片之前, 比较准备加载图片的url与ImageView中保存的最新图片的url是同一个,

如果不是同一个, 当前加载图片的任务不应该再执行

如果相同, 继续执行加载远程图片

c. 在主线程准备显示图片之前, 比较加载到图片的url与ImageView中保存的最新图片的url是同一个 如果不是同一个, 不需要显示此图片 如果相同, 显示图片

代码部分

首先准备20张图片的URL,我这里是随机找的20张美女图片,什么类型的都有,你们也可以自己找,风骚的性感的,可爱的,校花啥的,你开心就好。

public class ImageUrl {
public static final String image1="http://p4.so.qhmsg.com/bdr/_240_/t0157596cfec8884249.jpg";
public static final String image2="http://p3.so.qhimgs1.com/bdr/_240_/t0125644adcf2cb71d3.jpg";
public static final String image3="http://p3.so.qhimgs1.com/bdr/_240_/t01d31b4df909c2aa13.jpg";
public static final String image4="http://p0.so.qhmsg.com/bdr/_240_/t011fc15c16f31bb545.jpg";
public static final String image5="http://p4.so.qhmsg.com/bdr/_240_/t0187439344feb62039.jpg";
public static final String image6="http://p4.so.qhmsg.com/bdr/_240_/t01c867a331ee601f09.jpg";
public static final String image7="http://p1.so.qhimgs1.com/bdr/_240_/t01d207c6bb9e46c6b1.jpg";
public static final String image8="http://p0.so.qhmsg.com/bdr/_240_/t01d5ada99254f1b685.jpg";
public static final String image9="http://p0.so.qhimgs1.com/bdr/_240_/t019aa0ed00b08bc6ec.jpg";
public static final String image10="http://p1.so.qhmsg.com/bdr/_240_/t011a45058123590d96.jpg";
public static final String image11="http://p0.so.qhmsg.com/bdr/_240_/t015a2d44aeee1bcf7f.jpg";
public static final String image12="http://p1.so.qhimgs1.com/bdr/_240_/t01c55edc357b953b51.jpg";
public static final String image13="http://p1.so.qhimgs1.com/bdr/_240_/t017e236ece120c9721.jpg";
public static final String image14="http://p1.so.qhmsg.com/bdr/_240_/t01c9af3809a7b74ec6.jpg";
public static final String image15="http://p3.so.qhmsg.com/bdr/_240_/t0169f347f8991857f8.jpg";
public static final String image16="http://p1.so.qhimgs1.com/bdr/_240_/t016333a4d8144d0681.jpg";
public static final String image17="http://p0.so.qhimgs1.com/bdr/_240_/t0123f85ae6dba80770.jpg";
public static final String image18="http://p2.so.qhimgs1.com/bdr/_240_/t010d8bc3205ced32a2.jpg";
public static final String image19="http://p1.so.qhmsg.com/bdr/_240_/t014976fa8e0ae97556.jpg";
public static final String image20="http://p1.so.qhimgs1.com/bdr/_240_/t01a846efd527e8acc1.jpg";
 }

图片三级缓存工具类封装

public class ImageLoader {
    // 用于缓存Bitmap对象
    private Map<String, Bitmap> cacheMap = new HashMap<String, Bitmap>();
    private Context context;
    private int loadingImageRes;
    private int errorImageRes;

    public ImageLoader(Context context, int loadingImageRes, int errorImageRes) {
        this.context = context;
        this.loadingImageRes = loadingImageRes;
        this.errorImageRes = errorImageRes;
    }


    // 加载图片并且显示
    public void loadImage(String imagePath, ImageView imageView) {
        //将需要显示的图片url保存到视图上
        imageView.setTag(imagePath);
        /**
         * 1). 根据url从一级缓存中取对应的bitmap对象 如果有, 显示(结束) 如果没有, 进入2)
         */
        Bitmap bitmap = getFromFirstCache(imagePath);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        /**
         * 2). 从二级缓存中查找: 得到文件名并在sd卡的缓存目录下加载对应的图片得到Bitmap对象 如果有: 显示, 缓存到一级缓存中(结束)
         * 如果没有, 进入3)
         *
         * /storage/sdcard/Android/data/packageName/files/图片文件名(xxx.jpg)
         */
        bitmap = getFromSecondCache(imagePath);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            cacheMap.put(imagePath, bitmap);
            return;
        }
        /**
         * 3). 显示代表提示正在加载的图片, 启动分线程联网请求得到Bitmap对象 如果没有: 显示提示错误的图片(结束) 如果有:
         * 缓存到一级缓存(分线程) 缓存到二级缓存(分线程) 显示(主线程)
         */
        loadBitmapFromThirdCache(imagePath, imageView);
    }


    /**
     * 根据图片url从三级缓存中取对应的bitmap对象并显示
     *
     * @param imagePath
     * @param imageView AsyncTask loadBitmapFromThirdCache("../b.jpg", imageView)
     *                  loadBitmapFromThirdCache("../f.jpg",
     *                  imageView)--->imageView.setTag("../f.jpg")
     */
    private void loadBitmapFromThirdCache(final String imagePath,
                                          final ImageView imageView) {
        new AsyncTask<Void, Void, Bitmap>() {
            // 图片出现之前加载loading
            @Override
            protected void onPreExecute() {
                imageView.setImageResource(loadingImageRes);
            }


            // 请求网络得到图片
            @Override
            protected Bitmap doInBackground(Void... params) {
                Bitmap bitmap = null;
                try {
                    //在准备请求服务器图片之前, 判断是否需要加载
                    String newImagePath = (String) imageView.getTag();
                    if (newImagePath != imagePath) {//在准备请求服务器图片之前, 判断是否需要加载
                        return null;
                    }
                    // 得到连接
                    URL url = new URL(imagePath);
                    HttpURLConnection conn = (HttpURLConnection) url
                            .openConnection();
                    // 设置
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(5000);
                    // 连接
                    conn.connect();
                    // 发请求读取返回的数据并封装为bitmap
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream inputStream = conn.getInputStream();
                        // 将is封装为bitmap
                        bitmap = BitmapFactory.decodeStream(inputStream);
                        // 关闭
                        inputStream.close();
                        if (bitmap != null) { // 缓存到一级缓存(分线程)
                            cacheMap.put(imagePath, bitmap);
                            // 缓存到二级缓存(分线程) // /storage/sdcard/Android/data/packageName/files/
                            String filesPath = context.getExternalCacheDir().getAbsolutePath();
                            // 截取到/xx.jpg
                            String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
                            String filePath = filesPath + fileName;
                            // 缓存到二级缓存中
                            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath));
                        }
                    }
                    conn.disconnect();
                } catch (Exception e) {
                    e.printStackTrace();

                }
                return bitmap;
            }


            @Override
            protected void onPostExecute(Bitmap bitmap) {
                //从联网请求图片到得到图片对象需要一定的时间, 视图可能被复用了,不需要显示
                //在主线程准备显示图片之前, 需要判断是否需要显示
                String newImagePath = (String) imageView.getTag();
                if (newImagePath != imagePath) {//视图已经被复用了
                    return;
                }
                // 如果没有: 显示提示错误的图片(结束)
                //如果没有: 显示提示错误的图片(结束)
                if (bitmap == null) {
                    imageView.setImageResource(errorImageRes);
                } else {//如果有, 显示
                    imageView.setImageBitmap(bitmap);
                }
            }
        }.

                execute();


    }


    /**
     * 根据图片url从二级缓存中取对应的bitmap对象
     *
     * @param imagePath
     * @return
     */

    private Bitmap getFromSecondCache(String imagePath) {
        // /storage/sdcard/Android/data/packageName/files/
        String filesPath = context.getExternalCacheDir().getAbsolutePath();
        // String iamgePath =
        // https://ps.ssl.qhimg.com/sdmt/89_135_100/t015c74e0a1e3eaf370.jpg
        String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
        String filePath = filesPath + "/" + fileName;
        Bitmap bitmap = BitmapFactory.decodeFile(filePath);
        return bitmap;
    }


    /**
     * 根据图片url从一级缓存中取对应的bitmap对象
     *
     * @param imagePath
     * @return
     */
    private Bitmap getFromFirstCache(String imagePath) {
        // TODO Auto-generated method stub
        return cacheMap.get(imagePath);
    }
}

适配器的getView中使用:

    @Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if (convertView == null) {
        convertView = View.inflate(parent.getContext(), R.layout.item, null);
        holder = new ViewHolder();
        holder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    ImageLoader imageLoader = new ImageLoader(MainActivity.this, R.drawable.loading, R.drawable.error);
    imageLoader.loadImage(images.get(position), holder.imageView);
    return convertView;
}