详解三级缓存

三级缓存的好处

联网操作会耗费用户流量,而当我们加载完一张图片后本就不该重复加载,这时候我们就该考虑将图片缓存起来,节约用户的流量。

缓存的方式有三种:

1.网络缓存。我们初次获取图片需要从网络上下载,云服务就是一种网络缓存,网络相当于一个大容器,装载了我们需要的资源。这种方式速度慢,且耗费流量。

2.本地缓存。我们可以将从网上获取的资源存进手机的SD卡中,需要的时候再取出来,这种方式速度较快,可以起到节约流量的作用。

3.内存缓存。我们将从网上获取的资源存入系统分配给应用的内存中,这种方式速度最快,但是要防止oom(out of memory)异常,这时候我们需要用到LRUCache来避免这个问题。

以下为工具类:

1.内存缓存工具类:

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

/**
 * Created by ZhengFei on 2016/12/28.
 * 内存缓存工具类
 */

public class MemoryCacheUtils {
    //系统为每个应用分配的内存控件都是有限的,Runtime.getRuntime().maxMemory()获取了分配内存之后除以8当作我们用作缓存图片的内存大小
    public static final int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
    //强引用会造成内存溢出,而软引用在2.3版本之后系统会优先考虑回收,所以用官方推荐的LruCache
    public LruCache<String, Bitmap> lruCache = new LruCache<String, Bitmap>(maxSize) {
        /**计算出图片的大小*/
        @Override
        protected int sizeOf(String key, Bitmap value) {
            //value.getByteCount()
            //获取每一行的byte数,乘以value的高度就可以得出value的总byte啦
            int byteCount = value.getRowBytes() * value.getHeight();
            return byteCount;
        }
    };

    /**向lruCache中以bitmap的url为键,以bitmap为值进行存储*/
    public void storageBitmapInMemory(String url, Bitmap bitmap) {
        lruCache.put(url,bitmap);
    }

    /**从lruCache中以url为键,取出bitmap*/
    public Bitmap getBitmapFromMemory(String url) {
        return lruCache. get(url);
    }
}

2.本地缓存工具类:

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

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

import static android.graphics.Bitmap.CompressFormat.JPEG;

/**
 * Created by Administrator on 2016/12/28.
 * SD卡缓存工具类
 */

public class SDCardCacheUtils {

    public static final String CACHE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/cachePath";

    /**向SD卡中存储bitmap*/
    public void storageBitMapInSD(String url, Bitmap bitmap) {
        //以图片url文件名创建一个文件,由于文件名中不能带/,所以要把url进行MD5加密
        File files = new File(url);
        File file = new File(CACHE_PATH, MD5Utils.encryptMD5(url));
        //若文件不存在,则创造一个该名文件,若存在则直接用
        if(!file.exists()){
            file.mkdir();
        }
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
        } catch (FileNotFoundException e) { //其实这一步我们已经在上面判断过了,但系统强制要求带上,所以没办法咯
            e.printStackTrace();
        }
        //将bitmap压缩到输出流中,写入到文件里
        bitmap.compress(JPEG,100,fos);
    }

    /**从SD卡中获取bitmap*/
    public Bitmap getBitMapFromSD(String url) {
        File file = new File(CACHE_PATH, MD5Utils.encryptMD5(url));
        Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
        return bitmap;
    }
}

3.网络缓存工具类:

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

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

/**
 * Created by Administrator on 2016/12/28.
 */

public class NetCacheUtils {


    private ImageView mImageView;
    private String mUrl;
    private Activity mActivity;
    private MemoryCacheUtils mMemoryCacheUtils;
    private SDCardCacheUtils mSDCardCacheUtils;

    public NetCacheUtils(Activity activity, MemoryCacheUtils memoryCacheUtils, SDCardCacheUtils sDCardCacheUtils) {
        mActivity = activity;
        mMemoryCacheUtils = memoryCacheUtils;
        mSDCardCacheUtils = sDCardCacheUtils;
    }

    /**进行异步任务*/
    public void executeNetWork(ImageView imageView, String url) {
        new MyAsyncTask().execute(imageView, url); //启动AsyncTask
    }

    /**
     * 第一个参数 : 异步任务执行时传入的参数,对应 doInBackground()
     * 第二个参数 : 异步任务执行时的进度,对应 onProgressUpdate()
     * 第三个参数 : 异步任务执行后返回的结果类型,对应 onPostExecute()
     */
    class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> {

        /**处理联网操作,在子线程中*/
        @Override
        protected Bitmap doInBackground(Object... params) {
            //调用execute()时,传入的第一个参数是ImageView,第二个参数是URL
            mImageView = (ImageView) params[0];
            mUrl = (String) params[1];
            //给每个ImageView设置对应的url,防止图片错位
            mActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mImageView.setTag(mUrl);
                }
            });
            Bitmap bitmap = download(mUrl);
            return bitmap;
        }

        /**联网操作结束调用此方法,在主线程中*/
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if(bitmap != null){
                String url = (String) mImageView.getTag();
                if(url.equals(mUrl)){
                    mImageView.setImageBitmap(bitmap);
                    //将bitmap存入内存缓存和本地缓存中
                    mMemoryCacheUtils.storageBitmapInMemory(url, bitmap);
                    mSDCardCacheUtils.storageBitMapInSD(url, bitmap);
                }
            }
            super.onPostExecute(bitmap);
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }
    }

    /**进行联网操作,若对应的url中有bitmap,则返回并存储进内存缓存和本地缓存中,若没有则返回null*/
    private Bitmap download(String url) {

        HttpURLConnection urlConnection = null;
        try {
            URL uri = new URL(url);
            urlConnection = (HttpURLConnection) uri.openConnection();
            urlConnection.setRequestMethod("GET");
            urlConnection.setConnectTimeout(5000);
            urlConnection.setReadTimeout(5000);
            urlConnection.connect();
            int responseCode = urlConnection.getResponseCode();
            if(responseCode == 200){
                InputStream is = urlConnection.getInputStream();
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 2; //宽高压缩为原图的1/2
                options.inPreferredConfig = Bitmap.Config.ARGB_4444;
                Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);
                return bitmap;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            urlConnection.disconnect();
        }
        return null;
    }
}


4.图片加载工具类:


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

/**
 * Created by ZhengFei on 2016/12/29.
 * 图片加载工具类
 */

public class MyBitmapUtils {
    /**内存缓存工具类*/
    private MemoryCacheUtils mMemoryCacheUtils;
    /**本地缓存工具类*/
    private SDCardCacheUtils mSDCardCacheUtils;
    /**网络缓存工具类*/
    private NetCacheUtils mNetCacheUtils;

    public MyBitmapUtils(MemoryCacheUtils mMemoryCacheUtils, SDCardCacheUtils mSDCardCacheUtils, NetCacheUtils mNetCacheUtils) {
        this.mMemoryCacheUtils = mMemoryCacheUtils;
        this.mSDCardCacheUtils = mSDCardCacheUtils;
        this.mNetCacheUtils = mNetCacheUtils;
    }

    /**展示图片*/
    public void display(String url, ImageView iv) {

        Bitmap bitmapFromMemory = mMemoryCacheUtils.getBitmapFromMemory(url);
        //若内存缓存中有则从内存缓存中取
        if(bitmapFromMemory != null){
            iv.setImageBitmap(bitmapFromMemory);
            return;
        }

        //内存中没有则从本地中取
        Bitmap bitMapFromSD = mSDCardCacheUtils.getBitMapFromSD(url);
        if(bitMapFromSD != null){
            iv.setImageBitmap(bitMapFromSD);
            //取完后将bitmap存入内存缓存中
            mMemoryCacheUtils.storageBitmapInMemory(url,bitMapFromSD);
            return;
        }

        //内存和本地中都没有,从联网下载图片,并将获取的bitmap存入内存缓存和本地缓存中
        mNetCacheUtils.executeNetWork(iv,url);
    }
}

5.MD5工具类:


/**
 * Created by ZhengFei on 2016/12/28.
 */

import java.security.MessageDigest;

public class MD5Utils {

    /**
     * MD5加密
     * @param url
     * @return
     */
    public static String encryptMD5(String url) {
        try {
            MessageDigest digest = MessageDigest.getInstance("md5");
            byte[] result = digest.digest(url.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : result) {
                int number = b & 0xff - 3;// 加盐
                String str = Integer.toHexString(number);
                if (str.length() == 1) {
                    sb.append("0");
                }
                sb.append(str);
            }
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
    }
}

至此,图片的三级缓存基本就处理完了,长路漫漫,无心睡眠,继续沿着大神的脚步慢慢前进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值