是时候来一发了-----三级~~~缓存

咳咳咳,三级,咳咳。。缓存,最近感冒了嗓子不太好,打字也断断续续的了,(●’◡’●),言归正传,这里想主要记录下网络的三级缓存的原理,以及代码简单实现;

首先这里是缓存的原理图:
这里写图片描述

从上面的图中,我们可以知道的下面几件事情:
- 三级缓存指的是,内存,本地,网络;
- app如果想想要访问网络数据的话,最优先去访问内存,其次是本地,最后是网络(其响应的速度也是这个顺序)
- 如果app访问内存得到了数据,就不会再去本地和网络去请求,如果是在本地得到了数据,不仅是会将数据加载返回给app,而且会在内存中备份一份;同理网络中也是一样;

基于上面的原理,这里我创建一个用于缓存图片的一个类MyBitmapUtils里面的代码如果下:

public class MyBitmapUtils {

NetCacheUtils  netCacheUtils;
private LocalCacheUtils localCacheUtils;
private MemoryCacheUtils memoryCacheUtils;

public MyBitmapUtils() {
    //网络缓存
    netCacheUtils=new NetCacheUtils();
    //本地缓存
    localCacheUtils = new LocalCacheUtils();
    //内存缓存
    memoryCacheUtils = new MemoryCacheUtils();
}
/**
 * 通过URL我们,将得到的图片资源放到img中;
 * @param img
 * @param uri
 */
public  void  display(ImageView  img, String  url){
    //这里我们需要先访问一下本地看看有没有,但是我们实现的顺序的话,是需要先从网络,本地,内存这样的顺序:
    //这我们可以先为其设置一张默认的图片:
    img.setImageResource(R.drawable.pic_item_list_default);

    //这里我们先读取内存中的缓存,如果存在的话我们就结束
    Bitmap bitmapFromMemory = memoryCacheUtils.getBitmapFromMemory(url);
    if(bitmapFromMemory!=null){
        System.out.println("从内存中加载。。。。");
        img.setImageBitmap(bitmapFromMemory);
    }else {
        //内存中缓存的没有,我们就需要读取本地缓存;
        Bitmap bitmapFromLocal = localCacheUtils.getBitmapFromLocal(url);
        if(bitmapFromLocal!=null){
            System.out.println("从本地中加载。。。。");
            img.setImageBitmap(bitmapFromLocal);
        }else{
            //如果本地缓存也不存在的话,我们就需要访问一下网络了
            netCacheUtils.getBitmapFromNet(img,url);
            System.out.println("从网络中加载。。。。");
        }
    }
}

}

下面是NetCacheUtils的源码:

public class NetCacheUtils {

ImageView iv;
String url;
private LocalCacheUtils localCacheUtils;
private MemoryCacheUtils memoryCacheUtils;


public NetCacheUtils() {
    super();
    localCacheUtils = new LocalCacheUtils();
    memoryCacheUtils = new MemoryCacheUtils();
}
public  void getBitmapFromNet(ImageView iv,String url) {
    // TODO Auto-generated method stub
    //注意这里的话,我们是不能这样写的我们需要将参数传递过去否则就不能完全显示,后面的IV将前面的给覆盖了,如果我们传递引用d话,就不会出现这样的结果;
    BitmapTask task = new BitmapTask(iv);
    task.execute(url);
    this.url=url;
}
/**
 * AsyncTask:三个参数的作用:
 * 第一个参数表示的意思就是doInBackgroun的参数的类型;
 * 第二个参数表示的意思就是:onProgressUpdate的参数的类型:
 * 第三个参数表示的意思就是:doInBackground的返回值类型,和onPostExecute的参数的类型;
 * AsynTask表示的是异步加载框架,当我们调用
 * @author andy
 */
class BitmapTask extends AsyncTask<String, Void, Bitmap>{
    ImageView iv;
    public BitmapTask(ImageView iv) {
        // TODO Auto-generated constructor stub
        this.iv=iv;
    }
    //这个是在子线程中进行的;
    @Override
    protected Bitmap doInBackground(String... params) {
        // TODO Auto-generated method stub
        String path=params[0];
        URL url;
        try {
            url=new URL(path);
            HttpURLConnection conn= (HttpURLConnection)url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.setRequestMethod("GET");
            conn.connect();
            if(conn.getResponseCode()==200){
                //表示连接的成功:
                InputStream is = conn.getInputStream();
                Bitmap decodeStream = BitmapFactory.decodeStream(is);
                //这里有没有可能就是我们还在加载,但是ListView里面的图片已经被复用了?所以这里的话,我们要注意一下啊;
                iv.setTag(decodeStream);
                return decodeStream;
            }
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
    //这个是在主线中进行;这个方法参数就是doInBackground返回的值
    @Override
    protected void onPostExecute(Bitmap result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
        //防止,还没加载出来的时候,img就被复用了
        Bitmap map= (Bitmap)iv.getTag();
        if(map==result){
            iv.setImageBitmap(result);
            //**这里我们的话,我们是需要将Bitmap缓存到本地的;**
            localCacheUtils.setBitmapToLocal(url, result);
            //**将bitmap缓存在内存中:**
            memoryCacheUtils.setBitmapToMemory(url, result);
        }
    }
    //这个方法也是在主线程中进行,主要的作用就是对doInBackground里面的进度进行一个更新;
    @Override
    protected void onProgressUpdate(Void... values) {
        // TODO Auto-generated method stub
        super.onProgressUpdate(values);
    }
}
}

LocalCacheUtils 的话,就是一个文件的读写,这里的话,就不再罗列;
MemoryCacheUtils源码:

public class MemoryCacheUtils {

//这里我们要使用LruCache来实现对bitmap进行缓存,为什么要这么后面有讲解
private static LruCache<String, Bitmap> lruCache;

public MemoryCacheUtils() {
//获取手机的堆内存
long maxMemory = Runtime.getRuntime().maxMemory();
//设置最大的缓存容量;一般来说是手机堆内存的1/8
lruCache=new LruCache<String, Bitmap>((int) (maxMemory/8)){
    //设置每一个,对象占用的内存;
    @Override
    protected int sizeOf(String key, Bitmap value) {
        // TODO Auto-generated method stub
        return value.getByteCount();
    }
};
}   
//取出一个Bitmap对象;
public Bitmap getBitmapFromMemory(String url){
    return  lruCache.get(url);
}
//存入一个bitMap对象;
public void setBitmapToMemory(String url,Bitmap bitmap){
    lruCache.put(url, bitmap);
}

}

关于内存缓存这里有几点要说明:
1、我们一般缓存对象都会使用LruCache来缓存,这个类和Map类似,其底层实质就是一个HashMap,但是这里我们为什么不使用HashMap?因为HashMap虽然可以使用,但是不能限制大小,由于是强引用,java中的垃圾回收是不会去管的,当图片过多的时候,就会发生oom异常;
2、 为什么不用软引用,将Bitmap引用强度变弱,当内存不足的时候,让虚拟机回收,在安卓2.3以后,Dvm会去回收所有软引用,即使内存充足,而且在安卓3.1以后,谷歌推荐使用LruCache;
3、设置最大缓存容量,为甚要做这个,在第一条中说过,不使用HashMap的原因就是不能限制大小,而Lru是可以限制大小的,当达到最大缓存容量的时候,LruCache就会采用Lru算法将,过时的BitMap从LruCache中移出
4、我们重写了LruCache里面的SizeOf是为什么?因为我们存放的Bitmap每一个对象所占用的内存是不一样的,LruCache计算是否超过了设置的容量,需要根据每一个对象,及其所占内存来计算;
5、我们的LruCache必须是静态的,否者是其不到缓存的作用的;

好了,今天的三级缓存就到这里吧。。。

安卓交流群 :232748032

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值