咳咳咳,三级,咳咳。。缓存,最近感冒了嗓子不太好,打字也断断续续的了,(●’◡’●),言归正传,这里想主要记录下网络的三级缓存的原理,以及代码简单实现;
首先这里是缓存的原理图:
从上面的图中,我们可以知道的下面几件事情:
- 三级缓存指的是,内存,本地,网络;
- 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