三级缓存的好处是:可以尽量避免内存溢出,提高程序执行效率,省流量,提供用户体验
- 内存缓存: 最快
- 本地缓存: 其次
- 网络缓存: 速度最慢
- 三级缓存设计步骤:
* 从内存中取图片
* 从本地文件中取图片
向内存中保持一份
* 请求网络图片,获取图片,显示到控件上
* 向内存存一份
* 向本地文件中存一份
首先是内存缓存MemoryCacheUtils:
public class MemoryCacheUtils {
//集合
private LruCache<String, Bitmap> lruCache;
public MemoryCacheUtils(){
//使用系统分配给应用最大内存的八分之一作为缓存大小
int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8 / 1024);
lruCache = new LruCache<String, Bitmap>(maxSize){
@Override
protected int sizeOf(String key, Bitmap value) {
// return super.sizeOf(key, value);
return (value.getRowBytes() * value.getByteCount()) / 1024;
}
};
}
//根据Url从内存中获取图片
public Bitmap getBitmapFromUrl(String imageUrl) {
return lruCache.get(imageUrl);
}
//根据Url保存图片到lruCache集合中去
public void putBitmap(String imageUrl, Bitmap bitmap) {
lruCache.put(imageUrl, bitmap);
}
}
然后是本地缓存LocalCacheUtils:
public class LocalCacheUtils {
private final MemoryCacheUtils memoryCacheUtils;
public LocalCacheUtils(MemoryCacheUtils memoryCacheUtils) {
this.memoryCacheUtils = memoryCacheUtils;
}
/**
* 根据Url获取图片
* @param imageUrl
* @return
*/
public Bitmap getBitmapFromUrl(String imageUrl) {
//判断sdcard是否挂载
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
//保存图片在/mnt/sdcard/beijingnews/http://192.168.21.165:8080/xsxxxx.png
//保存图片在/mnt/sdcard/beijingnews/llkskljskljklsjklsllsl
try {
String fileName = MD5Encoder.encode(imageUrl);//llkskljskljklsjklsllsl
///mnt/sdcard/beijingnews/llkskljskljklsjklsllsl
File file = new File(Environment.getExternalStorageDirectory()+"/beijingnews",fileName);
if(file.exists()){
FileInputStream is = new FileInputStream(file);
Bitmap bitmap = BitmapFactory.decodeStream(is);
if(bitmap != null){
memoryCacheUtils.putBitmap(imageUrl,bitmap);
LogUtils.e("把从本地保持到内存中");
}
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
LogUtils.e("图片获取失败");
}
}
return null;
}
/**
* 根据Url保存图片
* @param imageUrl url
* @param bitmap 图片
*/
public void putBitmap(String imageUrl, Bitmap bitmap) {
//判断sdcard是否挂载
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
//保存图片在/mnt/sdcard/beijingnews/http://192.168.21.165:8080/xsxxxx.png
//保存图片在/mnt/sdcard/beijingnews/llkskljskljklsjklsllsl
try {
String fileName = MD5Encoder.encode(imageUrl);//llkskljskljklsjklsllsl
///mnt/sdcard/beijingnews/llkskljskljklsjklsllsl
File file = new File(Environment.getExternalStorageDirectory()+"/beijingnews",fileName);
File parentFile = file.getParentFile();//mnt/sdcard/beijingnews
if(!parentFile.exists()){
//创建目录
parentFile.mkdirs();
}
if(!file.exists()){
file.createNewFile();
}
//保存图片
bitmap.compress(Bitmap.CompressFormat.PNG,100,new FileOutputStream(file));
} catch (Exception e) {
e.printStackTrace();
LogUtils.e("图片本地缓存失败");
}
}
}
}
最后是网络缓存NetCacheUtils :
public class NetCacheUtils {
//请求图片成功
public static final int SUCESS = 1;
//请求图片失败
public static final int FAIL = 2;
private final Handler handler;
private final LocalCacheUtils localCacheUtils; //本地缓存工具类
private final MemoryCacheUtils memoryCacheUtils;//内存缓存工具类
private ExecutorService service; //线程池类
public NetCacheUtils(Handler handler, LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) {
this.handler = handler;
service = Executors.newFixedThreadPool(10);//创建固定线程池,容量为10
this.localCacheUtils = localCacheUtils;
this.memoryCacheUtils = memoryCacheUtils;
}
//联网请求得到图片
public void getBitmapFromNet(String imageUrl, int position) {
// new Thread(new MyRunnable(imageUrl, position)).start();
service.execute(new MyRunnable(imageUrl, position));
}
class MyRunnable implements Runnable{
private final String imageUrl;
private final int position;
public MyRunnable(String imageUrl, int position) {
this.imageUrl = imageUrl;
this.position = position;
}
@Override
public void run() {
//子线程
//请求网络图片
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET"); //只能大写
connection.setConnectTimeout(4000);
connection.setReadTimeout(4000);
connection.connect(); //可写可不写
int code = connection.getResponseCode();
if (code == 200){
InputStream is = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is); //把输入流转换成Bitmap
//显示到控件上,把Bitmap和position发出去
Message msg = Message.obtain();
msg.what = SUCESS;
msg.arg1 = position;
msg.obj = bitmap;
handler.sendMessage(msg);
//在内存中缓存一份
memoryCacheUtils.putBitmap(imageUrl, bitmap);
//在本地中缓存一份
localCacheUtils.putBitmap(imageUrl, bitmap);
}
} catch (IOException e) {
e.printStackTrace();
Message msg = Message.obtain();
msg.what = FAIL;
msg.arg1 = position;
handler.sendMessage(msg);
}
}
}
}
然后将三者集合到一个类中BitmapCacheUtils:
public class BitmapCacheUtils {
private NetCacheUtils netCacheUtils;//网络缓存工具类
private LocalCacheUtils localCacheUtils;//本地缓存工具类
private MemoryCacheUtils memoryCacheUtils;//内存缓存工具类
public BitmapCacheUtils(Handler handler){
memoryCacheUtils = new MemoryCacheUtils();
localCacheUtils = new LocalCacheUtils(memoryCacheUtils);
netCacheUtils = new NetCacheUtils(handler, localCacheUtils, memoryCacheUtils);
}
/*三级缓存设计步骤:
* 从内存中取图片
* 从本地文件中取图片
向内存中保持一份
* 请求网络图片,获取图片,显示到控件上,Handler,position
* 向内存存一份
* 向本地文件中存一份*/
public Bitmap getBitmap(String imageUrl, int position) {
//1.从内存选取图片
if (memoryCacheUtils != null){
Bitmap bitmap = memoryCacheUtils.getBitmapFromUrl(imageUrl);
if (bitmap != null){
LogUtils.e("内存加载图片成功——" + position);
return bitmap;
}
}
//2.从本地选取图片
if (localCacheUtils != null){
Bitmap bitmap = localCacheUtils.getBitmapFromUrl(imageUrl);
if (bitmap != null){
LogUtils.e("本地加载图片成功——" + position);
return bitmap;
}
}
//3.从网络选取图片
netCacheUtils.getBitmapFromNet(imageUrl, position);
return null;
}
}
最后,将文本缓存到SD卡中的方法:
//缓存文本数据
public static void putString(Context context, String key, String value) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//保存图片在/mnt/sdcard/beijingnews/http://192.168.0.103:8080/XSXXXX/png
//保存图片在/mnt/sdcard/beijingnews/hkjhsdhakdhasd(加密!!!)
try {
String fileName = MD5Encoder.encode(key);//llkskljskljklsjklsllsl
///mnt/sdcard/beijingnews/llkskljskljklsjklsllsl
File file = new File(Environment.getExternalStorageDirectory()+"/beijingnews/files",fileName);
File parentFile = file.getParentFile();//mnt/sdcard/beijingnews/files
if(!parentFile.exists()){
//创建目录
parentFile.mkdirs();
}
if(!file.exists()){
file.createNewFile();
}
//保存文本数据
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(value.getBytes());
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
LogUtils.e("文本本地缓存失败");
}
} else {
SharedPreferences sp = context.getSharedPreferences("atguitu", Context.MODE_PRIVATE);
sp.edit().putString(key, value).commit();
}
}
//缓存的文本信息
public static String getString(Context context, String key) {
String result = "";
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
try {
String fileName = MD5Encoder.encode(key);//llkskljskljklsjklsllsl
///mnt/sdcard/beijingnews/llkskljskljklsjklsllsl
File file = new File(Environment.getExternalStorageDirectory()+"/beijingnews/files",fileName);
if(file.exists()){
FileInputStream is = new FileInputStream(file);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) != -1){
stream.write(buffer, 0, length);
}
is.close();
stream.close();
result = stream.toString();
return result;
}
} catch (Exception e) {
e.printStackTrace();
LogUtils.e("图片获取失败");
}
} else {
SharedPreferences sp = context.getSharedPreferences("atguitu", Context.MODE_PRIVATE);
return sp.getString(key, ""); //这里用""空字符串,而不是null,不然会蹦
}
return result;
}