图片的三级缓存目的
主要目的是为了节省流量、加快加载速度;
每个 app 都会有大量的网络图片存在,当我们不做处理,每次打开 app 都去加载大量网络图片时,会耗费大量的流量,当网速不好时加载速度很慢;
三级缓存介绍以及优点
三级缓存即:内存缓存、本地缓存和网络缓存
内存缓存:加载速度快、不耗费流量、优先加载
本地缓存:加载速度快、不耗费流量、次级加载
网络缓存:加载速度慢、耗费流量、最后加载
三级缓存原理
首次打开 app 中的图片时,通过网络加载图片并将图片保存到本地和内存中。
再次打开 app 时,先从内存缓存中取出图片;如果没有,再从本地取出图片;如果还是没有说明本地图片被清理或者图片为新资源,采用网络加载图片。
下面是工具类代码和具体使用的 GitHub 下载地址:https://github.com/wuqingsen/PhotoCache
优快云 下载地址:https://download.youkuaiyun.com/download/wuqingsen1/11082536
创建工具类
图片缓存工具类:BitmapUtils ;
使用方法,第一个参数传 ImageView 控件;第二个参数传 String 类型图片链接:
BitmapUtils.getInstance().disPlay(imageView, url);
import android.graphics.Bitmap;
import android.util.Log;
import android.widget.ImageView;
import com.example.qd.photocache.R;
/**
* author: wu
* date: on 2019/4/2.
* describe:自定义的BitmapUtils,实现三级缓存
*/
public class BitmapUtils {
private NetCacheUtils mNetCacheUtils;
private LocalCacheUtils mLocalCacheUtils;
private MemoryCacheUtils mMemoryCacheUtils;
private static BitmapUtils bitmapUtils;
public static BitmapUtils getInstance() {
if (bitmapUtils == null) {
bitmapUtils = new BitmapUtils();
}
return bitmapUtils;
}
public BitmapUtils() {
mMemoryCacheUtils = new MemoryCacheUtils();
mLocalCacheUtils = new LocalCacheUtils();
mNetCacheUtils = new NetCacheUtils(mLocalCacheUtils, mMemoryCacheUtils);
}
public void disPlay(ImageView ivPic, String url) {
ivPic.setImageResource(R.mipmap.ic_launcher);
Bitmap bitmap;
//内存缓存
bitmap = mMemoryCacheUtils.getBitmapFromMemory(url);
if (bitmap != null) {
ivPic.setImageBitmap(bitmap);
Log.e("=====", "从内存获取图片");
return;
}
//本地缓存
bitmap = mLocalCacheUtils.getBitmapFromLocal(url);
if (bitmap != null) {
ivPic.setImageBitmap(bitmap);
Log.e("=====", "从本地获取图片");
//从本地获取图片后,保存至内存中
mMemoryCacheUtils.setBitmapToMemory(url, bitmap);
return;
}
//网络缓存
mNetCacheUtils.getBitmapFromNet(ivPic, url);
}
}
内存缓存工具类:MemoryCacheUtils
/**
* author: wu
* date: on 2019/4/2.
* describe:内存缓存
*/
public class MemoryCacheUtils {
private LruCache<String,Bitmap> mMemoryCache;
public MemoryCacheUtils(){
long maxMemory = Runtime.getRuntime().maxMemory()/8;
mMemoryCache=new LruCache<String,Bitmap>((int) maxMemory){
//用于计算每个条目的大小
@Override
protected int sizeOf(String key, Bitmap value) {
int byteCount = value.getByteCount();
return byteCount;
}
};
}
/**
* 从内存中读图片
* @param url
*/
public Bitmap getBitmapFromMemory(String url) {
Bitmap bitmap = mMemoryCache.get(url);
return bitmap;
}
/**
* 往内存中写图片
* @param url
* @param bitmap
*/
public void setBitmapToMemory(String url, Bitmap bitmap) {
mMemoryCache.put(url,bitmap);
}
}
本地缓存工具类 LocalCacheUtils :
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* author: wu
* date: on 2019/4/2.
* describe:本地缓存
*/
public class LocalCacheUtils {
private static final String CACHE_PATH= Environment.getExternalStorageDirectory().getAbsolutePath()+"/WerbNews";
/**
* 从本地读取图片
* @param url
*/
public Bitmap getBitmapFromLocal(String url){
String fileName = null;//把图片的url当做文件名,并进行MD5加密
try {
fileName = MD5Encoder.encode(url);
File file=new File(CACHE_PATH,fileName);
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 从网络获取图片后,保存至本地缓存
* @param url
* @param bitmap
*/
public void setBitmapToLocal(String url,Bitmap bitmap){
try {
String fileName = MD5Encoder.encode(url);//把图片的url当做文件名,并进行MD5加密
File file=new File(CACHE_PATH,fileName);
//通过得到文件的父文件,判断父文件是否存在
File parentFile = file.getParentFile();
if (!parentFile.exists()){
parentFile.mkdirs();
}
//把图片保存至本地
bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
}
}
网络缓存工具类 NetCacheUtils :
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* author: wu
* date: on 2019/4/2.
* describe:网络缓存
*/
public class NetCacheUtils {
private LocalCacheUtils mLocalCacheUtils;
private MemoryCacheUtils mMemoryCacheUtils;
public NetCacheUtils(LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) {
mLocalCacheUtils = localCacheUtils;
mMemoryCacheUtils = memoryCacheUtils;
}
/**
* 从网络下载图片
* @param ivPic 显示图片的imageview
* @param url 下载图片的网络地址
*/
public void getBitmapFromNet(ImageView ivPic, String url) {
new BitmapTask().execute(ivPic, url);//启动AsyncTask
}
/**
* AsyncTask就是对handler和线程池的封装
* 第一个泛型:参数类型
* 第二个泛型:更新进度的泛型
* 第三个泛型:onPostExecute的返回结果
*/
class BitmapTask extends AsyncTask<Object, Void, Bitmap> {
private ImageView ivPic;
private String url;
/**
* 后台耗时操作,存在于子线程中
* @param params
* @return
*/
@Override
protected Bitmap doInBackground(Object[] params) {
ivPic = (ImageView) params[0];
url = (String) params[1];
return downLoadBitmap(url);
}
/**
* 更新进度,在主线程中
* @param values
*/
@Override
protected void onProgressUpdate(Void[] values) {
super.onProgressUpdate(values);
}
/**
* 耗时方法结束后执行该方法,主线程中
* @param result
*/
@Override
protected void onPostExecute(Bitmap result) {
if (result != null) {
ivPic.setImageBitmap(result);
System.out.println("从网络缓存图片啦.....");
//从网络获取图片后,保存至本地缓存
mLocalCacheUtils.setBitmapToLocal(url, result);
//保存至内存中
mMemoryCacheUtils.setBitmapToMemory(url, result);
}
}
}
/**
* 网络下载图片
* @param url
* @return
*/
private Bitmap downLoadBitmap(String url) {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
//图片压缩
BitmapFactory.Options options = new BitmapFactory.Options();
// options.inSampleSize=2;//宽高压缩为原来的1/2
options.inPreferredConfig=Bitmap.Config.ARGB_4444;
Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream(),null,options);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
conn.disconnect();
}
return null;
}
}
上面用到了 MD5 加密,MD5 加密工具类:
import java.security.MessageDigest;
/**
* author: wu
* date: on 2019/4/2.
* describe:MD5加密
*/
public class MD5Encoder {
/**
* Md5Encoder
* @param string
*/
public static String encode(String string) throws Exception {
byte[] hash = MessageDigest.getInstance("MD5").digest(
string.getBytes("UTF-8"));
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10) {
hex.append("0");
}
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString();
}
}
演示代码
工具类添加完毕,演示一下三级缓存的具体使用;
新建 adapter ,在里面的每个 item 都去加载图片;列表长度为10,前五个加载同一张图片,后五个加载另一张图片,在 adapter 的 onBindViewHolder 中添加代码如下:
Log.e("=====position", position + "");
if (position > 5) {
BitmapUtils.getInstance().disPlay(holder.iv_icon, "http://static.gamemm.com/upload/avatar/201903/29/62018_1553847955.jpg");
} else {
BitmapUtils.getInstance().disPlay(holder.iv_icon, "http://static.gamemm.com/upload/record/201904/2/62511_1554183931_95789.jpg");
}
下面是第一次打开 app 打印日志,可以看出:
首次加载没有打印加载方式,默认是从网络加载图片;当图片加载完成后,因为图片链接一样,所以直接从内存中取出图片;
下面是关闭进程再次打开 app 的打印日志,可以看出:
进程被销毁,内存中图片也就没有了;所以从本地获取图片并存储到内存中;
这是源码 GitHub 下载地址:https://github.com/wuqingsen/PhotoCache