图片三级缓存
为什么要使用三级缓存
如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了
假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响
特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知
所以提出三级缓存策略,通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量
什么是三级缓存
网络缓存, 不优先加载, 速度慢,浪费流量
本地缓存, 次优先加载, 速度快
内存缓存, 优先加载, 速度最快
三级缓存原理
首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中
之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片
总之,只在初次访问新内容时,才通过网络获取图片资源
代码实现
- 新建一个BitmapUtils
package cn.iaapp.app.sixman.util;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.youth.banner.Banner;
import java.util.ArrayList;
import java.util.List;
/**
* 用来做三级缓存
* Created by 梁 on 2018/6/1.
*/
public class BitmapUtils {
private NetCacheUtils netCacheUtils ; //网络下载
private LocalUtils localUtils ; //本地下载
private final MemoryCacheUtils memoryCacheUtils;
private List<Bitmap> bitmapList = new ArrayList<>() ; //用于banner列表
public BitmapUtils() {
memoryCacheUtils = new MemoryCacheUtils();
localUtils = new LocalUtils(memoryCacheUtils) ;
netCacheUtils = new NetCacheUtils(localUtils,memoryCacheUtils);
}
/**
* 用来三级缓存显示图片
* @param imageView
* @param url
*/
public void disPlay(ImageView imageView , String url)
{
// 1. 内存缓存 速度很快不浪费流量 优先
Bitmap bitmapMemory = memoryCacheUtils.getMemoryCache(url);
if (bitmapMemory != null)
{
imageView.setImageBitmap(bitmapMemory);
return ;
}
// 2. 本地缓存 速度快 不浪费流量 其次
Bitmap bitmap = localUtils.getLocalCache(url);
if (bitmap != null)
{
imageView.setImageBitmap(bitmap);
return ;
}
// 3. 网络缓存 速度慢浪费流量 最后
netCacheUtils.getBitmapFromNet(imageView,url);
}
public void disPlayBanner(Banner banner , List<String> urlList){
// 1. 内存缓存 速度很快不浪费流量 优先
// for (int i = 0 ; i < urlList.size() ; i ++)
// {
// Bitmap bitmap = memoryCacheUtils.getMemoryCache(urlList.get(i));
// if (bitmap != null)
// bitmapList.add(bitmap) ;
// }
// if (bitmapList .size() != 0)
// {
// banner.setImages(bitmapList);
// return ;
// }
//
//// 2. 本地缓存 速度快 不浪费流量 其次
// for (int i = 0 ; i < urlList.size() ; i ++)
// {
// Bitmap bitmap = localUtils.getLocalCache(urlList.get(i));
// if (bitmap != null)
// bitmapList.add(bitmap) ;
// }
// if (bitmapList .size() != 0)
// {
banner.setImages(bitmapList);
// return ;
// }
// 3. 网络缓存 速度慢浪费流量 最后
netCacheUtils.getBitmapListFromNet(banner , urlList);
}
}
- 网络缓存
package cn.iaapp.app.sixman.util;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
import com.youth.banner.Banner;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* 网络缓存工具类
* Created by 梁 on 2018/6/1.
*/
public class NetCacheUtils {
private List <Bitmap> bitmapList = new ArrayList<>();
private LocalUtils localUtils ;
private MemoryCacheUtils memoryCacheUtils ;
public NetCacheUtils(LocalUtils localUtils, MemoryCacheUtils memoryCacheUtils) {
this.localUtils = localUtils ;
this.memoryCacheUtils = memoryCacheUtils ;
}
public void getBitmapFromNet(ImageView imageView , String url){
//异步下载图片
new BitmapTask().execute(imageView,url);
}
// 获取bitmap列表
public void getBitmapListFromNet(Banner banner, final List<String> urlList) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0 ; i < urlList.size() ; i ++)
{
Bitmap bitmap = download(urlList.get(i));
//写本地缓存 和内存缓存
localUtils.setLocalCache(bitmap,urlList.get(i));
memoryCacheUtils.setMemoryCache(urlList.get(i),bitmap);
bitmapList.add(bitmap) ;
}
}
}).start();
banner.setImages(bitmapList) ;
}
class BitmapTask extends AsyncTask<Object,Void ,Bitmap>
{
private ImageView imageView;
private String url;
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Bitmap doInBackground(Object... objects) {
imageView = (ImageView) objects[0];
url = (String) objects[1];
imageView.setTag(url); //与url 绑定一起
//使用url 下载图片
Bitmap bitmap = download(url);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
//给imagview 设置图片
//由于Listview 的重用机制,导致某个item有可能展示重用的那个item的图片导致图片凑乱
// 解决方案 确保当前设置的图片和当前显示的imageview 完全匹配
String url = (String) imageView.getTag();
if (this.url .equals(url))
{ //判断当前下载的图片的url 和imageurl 是否一致
if (result != null)
imageView.setImageBitmap(result);
//写本地缓存
localUtils.setLocalCache(result,url);
memoryCacheUtils.setMemoryCache(url,result);
}
}
}
//使用url 下载图片
private Bitmap download(String url) {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.connect();
int responseCode = conn.getResponseCode();
if (responseCode == 200)
{
InputStream inputStream = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap ;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
conn.disconnect();
}
return null ;
}
}
- 本地缓存
package cn.iaapp.app.sixman.util;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.List;
/**
* 本地缓存的工具类
* Created by 梁 on 2018/6/1.
*/
public class LocalUtils {
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/jft_Cache/" ; //缓存文件夹
private MemoryCacheUtils memoryCacheUtils ;
public LocalUtils(MemoryCacheUtils memoryCacheUtils) {
this.memoryCacheUtils = memoryCacheUtils ;
}
/**
* 写缓存
* @param bitmap 从网络下载得到的bitmap
* @param url 图片的url 作为图片的唯一标识
*/
public void setLocalCache(Bitmap bitmap ,String url) {
//将图片保存到本地文件
File dir = new File(path) ;
if(!dir.exists() || !dir.isDirectory())
{
dir.mkdirs() ; // 创建文件夹
}
try {
File cacheFile = new File(dir,MD5Util.md5String(url)+".JPEG") ;
bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(cacheFile)) ;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//读缓存
public Bitmap getLocalCache(String url){
File cacheFile = new File(path,MD5Util.md5String(url) + ".JPEG");
if (cacheFile.exists())
{
// 缓存路径
try {
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(cacheFile));
//写内存缓存
memoryCacheUtils.setMemoryCache(url,bitmap);
return bitmap ;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
return null ;
}
}
4.内存缓存
package cn.iaapp.app.sixman.util;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import java.lang.ref.SoftReference;
import java.util.HashMap;
/**
* 内存缓存
* Created by 梁 on 2018/6/1.
*/
public class MemoryCacheUtils {
private HashMap<String , SoftReference<Bitmap>> mHashMap = new HashMap<>() ;
private LruCache<String , Bitmap> mLruCache;
public MemoryCacheUtils( ) {
//获取虚拟机分配的最大内存
long maxMemory = Runtime.getRuntime().maxMemory();
//maxSize :内存上限
mLruCache = new LruCache<String,Bitmap>((int) (maxMemory / 8))
{
@Override // 用来返回单个对象占用内存的大小
protected int sizeOf(String key, Bitmap value) {
//计算图片占用内存的大小 // int byteCount = value.getByteCount();
int byteCount = value.getRowBytes() * value.getHeight();
return byteCount;
}
}; //一般给 2M 也就是 1/8
}
//写缓存
public void setMemoryCache(String url , Bitmap bitmap){
// SoftReference<Bitmap> bitmapSoftReference = new SoftReference<Bitmap>(bitmap); //用软引用包装
// mHashMap.put(url,bitmapSoftReference) ;
mLruCache.put(url,bitmap) ;
}
//读缓存
public Bitmap getMemoryCache(String url){
// SoftReference<Bitmap> soft = mHashMap.get(url);
// if (soft != null)
// {
// Bitmap bitmap = soft.get(); //从软引用中取出当前对象
// return bitmap;
// }
return mLruCache.get(url) ;
}
}
使用
BitmapUtils bitmapUtils = new BitmapUtils();
bitmapUtils.disPlay(mIamgeView,url);
Glide 的内存缓存
Glide 是默认开启了内存缓存的,只要你通过 Glide 加载一张图片,他就会缓存到内存中,只要他还没被从内存中清理之前,下次使用 Glide 都会从内存缓存中加载。大大提升了图片加载的效率。
当然如果你有特殊要求,可以添加一行代码把默认开启的内存缓存关闭掉。
?
1
2
3
4
Glide.with(this)
.load(url)
.skipMemoryCache(true)//关闭内存缓存
.into(imageView);
Glide 的内存缓存实际上和我们上面说的差别不大,使用的也是LruCache算法,不过他还结合了一种弱引用机制,共同完成了内存缓存功能。
Glide 的硬盘缓存
关于 Glide 硬盘缓存使用也是十分简单。
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.into(imageView);
一个 diskCacheStrategy( ) 方法就可以调整他的硬盘缓存策略。其中可以传入的参数有四种:
DiskCacheStrategy.NONE: 表示不缓存任何内容。
DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
Glide 的硬盘缓存是默认将图片压缩转换后再缓存到硬盘中,这种处理方式再避免OOM的时候会经常看见。
如果需要改变硬盘缓存策略只需要改变其传入的参数即可。
上一篇: 图片三级缓存
下一篇: 变量的解构赋值(对象)