浅谈图片载入的三级缓存(一)

之前被人问及过。图片的三级缓存是什么啊,来给我讲讲,图片三级缓存,好高大尚的名字,听着挺厉害,应该是非常厉害的技术。当时不会啊,也就没什么了。没有说出来呗,前一阶端用到了BitmapUtils的图片缓存框架,索性就自己找些知识点来研究一些图片的三级缓存是什么吧。真所谓是知识你要是不知道,那就真的说不出所以然来。可是当你真正的去了解了。三级缓存也不是那么高端的技术。

好了,闲话不多说了。開始图片的三级缓存原理吧。

什么是图片的三级缓存

  • 1、内存缓存 优先载入,速度最快
  • 2、本地缓存 次优先载入 速度稍快
  • 3、网络缓存 最后载入 速度由网络速度决定(浪费流量)

图片的三级缓存图形解释

好了,既然知道了什么三级缓存的字面意思了,那么我们就来处理吧。

图片缓存—内存缓存的原理

MemoryCacheUtils.java:

package com.example.bitmaputils.bitmap;

import android.graphics.Bitmap;
import android.util.Log;
import android.util.LruCache;

/**
 * 内存缓存
 */
public class MemoryCacheUtils {

    /**
     * LinkedHashMap<>(10,0.75f,true);
     * <p/>
     * 10是最大致   0.75f是载入因子   true是訪问排序   false插入排序
     *
     *
     */
    //private LinkedHashMap<String,Bitmap> mMemoryCache = new LinkedHashMap<>(5,0.75f,true);

    private LruCache<String, Bitmap> mLruCache;

    public MemoryCacheUtils() {
        long maxMemory = Runtime.getRuntime().maxMemory();//最大内存  默认是16兆  执行时候的
        mLruCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                //int byteCount = value.getByteCount();
                //得到图片字节数
                // @return number of bytes between rows of the native bitmap pixels.
                int byteCount = value.getRowBytes() * value.getWidth();
                return byteCount;
            }
        };
    }

    /**
     * 从内存中读取
     *
     * @param url
     */
    public Bitmap getFromMemroy(String url) {

        Log.d("MyBitmapUtils", "从内存中载入图片");
        return mLruCache.get(url);
    }

    /**
     * 写入到内存中
     *
     * @param url
     * @param bitmap
     */
    public void setToMemory(String url, Bitmap bitmap) {
        mLruCache.put(url, bitmap);
    }
}

非常easy吧。在这里我们使用了LruCache(),对图片进行了内存缓存,这里我们仅仅是略微进行了处理,在这里我们仅仅是介绍原理,当然了哈,性能,OOM溢出问题我们在这里不作处理。

图片缓存—本地缓存

package com.example.bitmaputils.bitmap;

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

import com.example.bitmaputils.MD5Encoder;

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

/**
 * 本地缓存
 */
public class SDcardCacheUtils {

    /**
     * 我们读取内存的绝对路径
     */
    public static final String CACHE_PATH = Environment
            .getExternalStorageDirectory().getAbsolutePath() + "/aixuexi";

    /**
     * 从本地读取
     * @param url
     */
    public Bitmap getFromSd(String url){
        String fileName = null;
        try {
            //得到图片的url的md5的文件名称
            fileName = MD5Encoder.encode(url);
        } catch (Exception e) {
            e.printStackTrace();
        }
        File file = new File(CACHE_PATH,fileName);

        //假设存在。就通过bitmap工厂,返回的bitmap。然后返回bitmap
        if (file.exists()){
            try {
                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
                Log.d("MyBitmapUtils", "从本地读取图片啊");
                return bitmap;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 向本地缓存
     *
     * @param url   图片地址
     * @param bitmap   图片
     */
    public void savaSd(String url,Bitmap bitmap){
        String fileName = null;
        try {
            //我们对图片的地址进行MD5加密,作为文件名称
            fileName = MD5Encoder.encode(url);
        } catch (Exception e) {
            e.printStackTrace();
        }

        /**
         * 以CACHE_PATH为目录  fileName为文件名称
         */
        File file = new File(CACHE_PATH,fileName);

        //我们首先得到他的符文剑
        File parentFile = file.getParentFile();
        //查看是否存在,假设不存在就创建
        if (!parentFile.exists()){
            parentFile.mkdirs(); //创建目录
        }

        try {
            //将图片保存到本地
            /**
             * @param format   The format of the compressed image   图片的保存格式
             * @param quality  Hint to the compressor, 0-100. 0 meaning compress for
             *                 small size, 100 meaning compress for max quality. Some
             *                 formats, like PNG which is lossless, will ignore the
             *                 quality setting
             *                 图片的保存的质量    100最好
             * @param stream   The outputstream to write the compressed data.
             */
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

图片缓存—网络缓存

NetCacheUtils .java:

package com.example.bitmaputils.bitmap;

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

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

/**
 * 网络缓存工具类
 */
public class NetCacheUtils {

    /**
     * 图片
     */
    private ImageView mImageView;

    /**
     * 图片地址
     */
    private String mUrl;

    /**
     * 本地缓存
     */
    private SDcardCacheUtils mDcardCacheUtils;

    /**
     * 内存缓存
     */
    private MemoryCacheUtils mMemoryCacheUtils;

    public NetCacheUtils(SDcardCacheUtils dcardCacheUtils, MemoryCacheUtils memoryCacheUtils) {
        mDcardCacheUtils = dcardCacheUtils;
        mMemoryCacheUtils = memoryCacheUtils;
    }

    /**
     * 从网络中下载图片
     *
     * @param image
     * @param url
     */
    public void getDataFromNet(ImageView image, String url) {
        new MyAsyncTask().execute(image, url);  //启动Asynctask,传入的參数到相应doInBackground()
    }

    /**
     * 异步下载
     * <p/>
     * 第一个泛型 : 參数类型  相应doInBackground()
     * 第二个泛型 : 更新进度   相应onProgressUpdate()
     * 第三个泛型 : 返回结果result   相应onPostExecute
     */
    class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> {

        /**
         * 后台下载  子线程
         *
         * @param params
         * @return
         */
        @Override
        protected Bitmap doInBackground(Object... params) {

            //拿到传入的image
            mImageView = (ImageView) params[0];

            //得到图片的地址
            mUrl = (String) params[1];
            //将imageview和url绑定,防止错乱
            mImageView.setTag(mUrl);

            Bitmap bitmap = downLoadBitmap(mUrl);

            return bitmap;
        }

        /**
         * 进度更新   UI线程
         *
         * @param values
         */
        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        /**
         * 回调结果。耗时方法结束后。主线程
         *
         * @param bitmap
         */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null) {
                //得到图片的tag值
                String url = (String) mImageView.getTag();
                //确保图片设置给了正确的image
                if (url.equals(mUrl)) {
                    mImageView.setImageBitmap(bitmap);

                    /**
                     * 当从网络上下载好之后保存到sdcard中
                     */
                    mDcardCacheUtils.savaSd(mUrl, bitmap);

                    /**
                     *  写入到内存中
                     */
                    mMemoryCacheUtils.setToMemory(mUrl, bitmap);
                    Log.d("MyBitmapUtils", "我是从网络缓存中读取的图片啊");
                }
            }
        }
    }

    /**
     * 下载图片
     *
     * @param url 下载图片地址
     * @return
     */
    private Bitmap downLoadBitmap(String url) {

        //连接
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(url)
                    .openConnection();

            //设置读取超时
            conn.setReadTimeout(5000);
            //设置请求方法
            conn.setRequestMethod("GET");
            //设置连接超时连接
            conn.setConnectTimeout(5000);
            //连接
            conn.connect();

            //响应码
            int code = conn.getResponseCode();

            if (code == 200) {  //请求正确的响应码是200
                //得到响应流
                InputStream inputStream = conn.getInputStream();
                //得到bitmap对象
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

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

        return null;
    }
}

好了,至此,一个简单的图片的三级缓存完毕了。接下来看看使用吧。

图片载入工具类

MyBitmapUtils .java:

package com.example.bitmaputils.bitmap;

/**
 * Created by 若兰 on 2016/1/29.
 * 一个懂得了编程乐趣的小白。希望自己
 * 能够在这个道路上走的非常远,也希望自己学习到的
 * 知识能够帮助很多其它的人,分享就是学习的一种乐趣
 * QQ:1069584784
 * csdn:http://blog.csdn.net/wuyinlei
 */

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

/**
 * 自己定义的bitmap工具类
 */
public class MyBitmapUtils {

    /**
     * 网络缓存
     */
    public NetCacheUtils mNetCacheUtils;

    /**
     * 本地缓存
     */
    public SDcardCacheUtils mSdCacheUtils;

    /**
     * 内存缓存
     */
    public MemoryCacheUtils mMemoryCacheUtils;

    public MyBitmapUtils() {
        mSdCacheUtils = new SDcardCacheUtils();
        mMemoryCacheUtils = new MemoryCacheUtils();
        mNetCacheUtils = new NetCacheUtils(mSdCacheUtils, mMemoryCacheUtils);
    }

    /**
     * 展示图片的方法
     *
     * @param image
     * @param url
     */
    public void display(ImageView image, String url) {

        //从内存中读取
        Bitmap fromMemroy = mMemoryCacheUtils.getFromMemroy(url);
        //假设内存中有的h话就直接返回。从内存中读取
        if (fromMemroy != null) {
            image.setImageBitmap(fromMemroy);

            return;
        }

        //从本地SD卡读取
        Bitmap fromSd = mSdCacheUtils.getFromSd(url);
        if (fromSd != null) {
            image.setImageBitmap(fromSd);

            mMemoryCacheUtils.setToMemory(url, fromSd);

            return;
        }
        //从网络中读取
        mNetCacheUtils.getDataFromNet(image, url);

    }
}

使用这个工具类就非常easy了

仅仅需在载入数据的适配器中

//声明图片载入工具类
private MyBitmapUtils utils;
        public PhotoAdapter() {
            //mBitmapUtils = new BitmapUtils(MainActivity.this);
           // mBitmapUtils.configDefaultLoadingImage(R.mipmap.defaut);
            utils = new MyBitmapUtils();
        }

然后在getView()方法中,使用工具类中的display()方法就能够了。简单吧
utils.display(holder.tvImage,mImageViews[position]);

在这里我们看下图片载入效果:

来看下LOGCAT日志:

01-29 11:19:48.127 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:19:48.395 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:19:48.687 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:19:49.282 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 从内存中载入图片
01-29 11:19:49.628 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:19:50.173 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 我是从网络缓存中读取的图片啊
01-29 11:19:58.630 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 从内存中载入图片
01-29 11:19:58.762 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:19:59.325 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 我是从网络缓存中读取的图片啊
01-29 11:19:59.624 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊

01-29 11:56:40.897 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:56:40.923 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从内存中载入图片
01-29 11:56:41.105 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:56:41.118 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从内存中载入图片
01-29 11:56:41.268 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:56:41.283 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从内存中载入图片
01-29 11:56:41.431 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:56:43.837 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从内存中载入图片
01-29 11:56:44.013 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊
01-29 11:56:46.047 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从内存中载入图片
01-29 11:56:46.222 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 从本地读取图片啊

好了,一个简单的图片三级缓存原理就这种诞生了,当然正如開始所说,这里仅仅是简单的原理介绍。像里面用到的AsyncTask异步载入,和LruCache在这里仅仅是使用,在以后的篇章中会对这两个进行介绍的,好了。因为代码展示的代码量太多,这里我就不在上传怎么显示图片的源代码了。以下我会提供github源代码地址,能够找到我的这个demo的源代码。假设有问题,或者独特的见解,咱们能够讨论哦,QQ:1069584784

时间: 2024-10-01 00:37:50

浅谈图片载入的三级缓存(一)的相关文章

浅谈图片服务器的架构演进

浅谈图片服务器的架构演进 现在几乎任何一个网站.Web App以及移动APP等应用都需要有图片展示的功能,对于图片功能从下至上都是很重要的.必须要具有前瞻性的规划好图片服务器,图片的上传和下载速度至关重要,当然这并不是说一上来就搞很NB的架构,至少具备一定扩展性和稳定性.虽然各种架构设计都有,在这里我只是谈谈我的一些个人想法. 对于图片服务器来说IO无疑是消耗资源最为严重的,对于web应用来说需要将图片服务器做一定的分离,否则很可能因为图片服务器的IO负载导致应用 崩溃.因此尤其对于大型网站和应

Android进阶图片处理之三级缓存方案

图片的三级缓存 一.概述 一開始在学习Android的时候.处理图片的时候,每次获取图片都是直接从网络上面载入图片. 可是在开发项目的过程中,每次点击进入app里面,图片都要慢慢的再一次从网络上面载入. 给用户的体验很不好,第一个等待的时间很令人dan 疼 第二个给用户的流量造成了不必要的浪费 因此提出图片的三级缓存策略, 所谓的三级缓存:就是在手机载入图片的时候, 1.首先从内存中载入, 2.假设内存中没有的话,从sd卡上获取.读取到之后将图片写入到内存中 3.假设sd卡上没有的话,从网络上获

转:浅谈图片服务器的架构演进

现在几乎任何一个网站.Web App以及移动APP等应用都需要有图片展示的功能,对于图片功能从下至上都是很重要的.必须要具有前瞻性的规划好图片服务器,图片的上传和下载速度至关重要,当然这并不是说一上来就搞很NB的架构,至少具备一定扩展性和稳定性.虽然各种架构设计都有,在这里我只是谈谈我的一些个人想法. 对于图片服务器来说IO无疑是消耗资源最为严重的,对于web应用来说需要将图片服务器做一定的分离,否则很可能因为图片服务器的IO负载导致应用 崩溃.因此尤其对于大型网站和应用来说,非常有必要将图片服

浅谈数据库、JVM、缓存、SQL等性能调优方法和原则

性能优化基本是BAT等一线互联网公司程序员必备的技能,以下为大家完整揭晓性能完整的优化方案和方法:包含web网站调优.数据库.JVM调优.架构调优等方案. 第一:Web网站调优 1.尽可能减少HTTP请求:图片合并 (css sprites),Js脚本文件合并.css文件合并. 2.减少DNS查询 3.将css放在页面最上面,将js放在页面最下面 4.压缩js和css 减少文件体积,去除不必要的空白符.格式符.注释(即对代码进行格式化) 5.把js和css提取出来放在外部文件中 这一条要灵活运用

浅谈图片上传之剪切

对于一个网站来说,图片显示都是有一定的宽高比的,而客户上传的图片大多未经过剪切,故上传以后对图片进行一定的剪切是非常必要的. 如此,我们应当剪一个类来完成这项工作. public class ImageHelper{ ///图片宽高比,默认1.333 double _webWidth=1.333; /// <summary> ///网站显示图片的 宽/高比 /// </summary> public double WebWidth { get { return _webWidth;

简单地Android中图片的三级缓存机制

我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存缓存中,缓存到强引用中 也就是LruCache中.如果强引用中空间不足,就会将较早存储的图片对象驱逐到软引用(softReference)中存储,然后将图片缓存到文件(内部存储外部存储)中:读取图片的时候,先读取内存缓存,判断强引用中是否存在图片,如果强引用中存在,则直接读取,如果强引用中不存在,则

浅谈mysql innodb缓存策略

浅谈mysql innodb缓存策略: The InnoDB Buffer Pool Innodb 持有一个存储区域叫做buffer pool是为了在内存中缓存数据和索引,知道innodb bufferpool怎么工作,和利用它读取频繁访问的数据,是mysql优化重要的方面. 理想状况下,把bufferpool的大小调整到足够大,留下足够的内存空间给其他该服务器上的进程(使其无缺页即可).bufferpool越大,innodb 月表现为内存型数据库,从硬盘上一次读取数据,之后并成了从内存中读取数

浅谈浏览器的缓存机制

浏览器的缓存可分为HTTP缓存和离线缓存,下面将分别介绍 HTTP缓存 只有GET请求能被缓存,POST不能被缓存.Modified Time/ETag/Expires/Cache都是HTTP协议的缓存策略 先来一个例子 当我们第二次访问百度首页,在Chrome的Network面板中打开一个静态文件时会发现响应的status是:200 OK (from disk cache),不是应该返回304 Not Modified吗?如果你知道答案,那就可以忽略本文了. Cache-Control 简介

Android开源框架ImageLoader:加载图片的三级缓存机制

前言:可从  https://github.com/nostra13/Android-Universal-Image-Loader 下载三级缓存机制的开源框架.下文简单介绍该框架中主要的常用方法,掌握这些方法,基本就可应对多数图片下载的需求. 注意:以下代码为示意代码片断,仔细读一下应能知道怎么用.蓝色表示为开源框架中的类. 1.初始化ImageLoader类对象: ImageLoader imageLoader = ImageLoader.getInstance(); imageLoader.