三级缓存的好处
联网操作会耗费用户流量,而当我们加载完一张图片后本就不该重复加载,这时候我们就该考虑将图片缓存起来,节约用户的流量。
缓存的方式有三种:
1.网络缓存。我们初次获取图片需要从网络上下载,云服务就是一种网络缓存,网络相当于一个大容器,装载了我们需要的资源。这种方式速度慢,且耗费流量。
2.本地缓存。我们可以将从网上获取的资源存进手机的SD卡中,需要的时候再取出来,这种方式速度较快,可以起到节约流量的作用。
3.内存缓存。我们将从网上获取的资源存入系统分配给应用的内存中,这种方式速度最快,但是要防止oom(out of memory)异常,这时候我们需要用到LRUCache来避免这个问题。
以下为工具类:
1.内存缓存工具类:
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
/**
* Created by ZhengFei on 2016/12/28.
* 内存缓存工具类
*/
public class MemoryCacheUtils {
//系统为每个应用分配的内存控件都是有限的,Runtime.getRuntime().maxMemory()获取了分配内存之后除以8当作我们用作缓存图片的内存大小
public static final int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
//强引用会造成内存溢出,而软引用在2.3版本之后系统会优先考虑回收,所以用官方推荐的LruCache
public LruCache<String, Bitmap> lruCache = new LruCache<String, Bitmap>(maxSize) {
/**计算出图片的大小*/
@Override
protected int sizeOf(String key, Bitmap value) {
//value.getByteCount()
//获取每一行的byte数,乘以value的高度就可以得出value的总byte啦
int byteCount = value.getRowBytes() * value.getHeight();
return byteCount;
}
};
/**向lruCache中以bitmap的url为键,以bitmap为值进行存储*/
public void storageBitmapInMemory(String url, Bitmap bitmap) {
lruCache.put(url,bitmap);
}
/**从lruCache中以url为键,取出bitmap*/
public Bitmap getBitmapFromMemory(String url) {
return lruCache. get(url);
}
}
2.本地缓存工具类:
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import static android.graphics.Bitmap.CompressFormat.JPEG;
/**
* Created by Administrator on 2016/12/28.
* SD卡缓存工具类
*/
public class SDCardCacheUtils {
public static final String CACHE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/cachePath";
/**向SD卡中存储bitmap*/
public void storageBitMapInSD(String url, Bitmap bitmap) {
//以图片url文件名创建一个文件,由于文件名中不能带/,所以要把url进行MD5加密
File files = new File(url);
File file = new File(CACHE_PATH, MD5Utils.encryptMD5(url));
//若文件不存在,则创造一个该名文件,若存在则直接用
if(!file.exists()){
file.mkdir();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
} catch (FileNotFoundException e) { //其实这一步我们已经在上面判断过了,但系统强制要求带上,所以没办法咯
e.printStackTrace();
}
//将bitmap压缩到输出流中,写入到文件里
bitmap.compress(JPEG,100,fos);
}
/**从SD卡中获取bitmap*/
public Bitmap getBitMapFromSD(String url) {
File file = new File(CACHE_PATH, MD5Utils.encryptMD5(url));
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
return bitmap;
}
}
3.网络缓存工具类:
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Created by Administrator on 2016/12/28.
*/
public class NetCacheUtils {
private ImageView mImageView;
private String mUrl;
private Activity mActivity;
private MemoryCacheUtils mMemoryCacheUtils;
private SDCardCacheUtils mSDCardCacheUtils;
public NetCacheUtils(Activity activity, MemoryCacheUtils memoryCacheUtils, SDCardCacheUtils sDCardCacheUtils) {
mActivity = activity;
mMemoryCacheUtils = memoryCacheUtils;
mSDCardCacheUtils = sDCardCacheUtils;
}
/**进行异步任务*/
public void executeNetWork(ImageView imageView, String url) {
new MyAsyncTask().execute(imageView, url); //启动AsyncTask
}
/**
* 第一个参数 : 异步任务执行时传入的参数,对应 doInBackground()
* 第二个参数 : 异步任务执行时的进度,对应 onProgressUpdate()
* 第三个参数 : 异步任务执行后返回的结果类型,对应 onPostExecute()
*/
class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> {
/**处理联网操作,在子线程中*/
@Override
protected Bitmap doInBackground(Object... params) {
//调用execute()时,传入的第一个参数是ImageView,第二个参数是URL
mImageView = (ImageView) params[0];
mUrl = (String) params[1];
//给每个ImageView设置对应的url,防止图片错位
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mImageView.setTag(mUrl);
}
});
Bitmap bitmap = download(mUrl);
return bitmap;
}
/**联网操作结束调用此方法,在主线程中*/
@Override
protected void onPostExecute(Bitmap bitmap) {
if(bitmap != null){
String url = (String) mImageView.getTag();
if(url.equals(mUrl)){
mImageView.setImageBitmap(bitmap);
//将bitmap存入内存缓存和本地缓存中
mMemoryCacheUtils.storageBitmapInMemory(url, bitmap);
mSDCardCacheUtils.storageBitMapInSD(url, bitmap);
}
}
super.onPostExecute(bitmap);
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
}
/**进行联网操作,若对应的url中有bitmap,则返回并存储进内存缓存和本地缓存中,若没有则返回null*/
private Bitmap download(String url) {
HttpURLConnection urlConnection = null;
try {
URL uri = new URL(url);
urlConnection = (HttpURLConnection) uri.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setConnectTimeout(5000);
urlConnection.setReadTimeout(5000);
urlConnection.connect();
int responseCode = urlConnection.getResponseCode();
if(responseCode == 200){
InputStream is = urlConnection.getInputStream();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2; //宽高压缩为原图的1/2
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
return null;
}
}
4.图片加载工具类:
import android.graphics.Bitmap;
import android.widget.ImageView;
/**
* Created by ZhengFei on 2016/12/29.
* 图片加载工具类
*/
public class MyBitmapUtils {
/**内存缓存工具类*/
private MemoryCacheUtils mMemoryCacheUtils;
/**本地缓存工具类*/
private SDCardCacheUtils mSDCardCacheUtils;
/**网络缓存工具类*/
private NetCacheUtils mNetCacheUtils;
public MyBitmapUtils(MemoryCacheUtils mMemoryCacheUtils, SDCardCacheUtils mSDCardCacheUtils, NetCacheUtils mNetCacheUtils) {
this.mMemoryCacheUtils = mMemoryCacheUtils;
this.mSDCardCacheUtils = mSDCardCacheUtils;
this.mNetCacheUtils = mNetCacheUtils;
}
/**展示图片*/
public void display(String url, ImageView iv) {
Bitmap bitmapFromMemory = mMemoryCacheUtils.getBitmapFromMemory(url);
//若内存缓存中有则从内存缓存中取
if(bitmapFromMemory != null){
iv.setImageBitmap(bitmapFromMemory);
return;
}
//内存中没有则从本地中取
Bitmap bitMapFromSD = mSDCardCacheUtils.getBitMapFromSD(url);
if(bitMapFromSD != null){
iv.setImageBitmap(bitMapFromSD);
//取完后将bitmap存入内存缓存中
mMemoryCacheUtils.storageBitmapInMemory(url,bitMapFromSD);
return;
}
//内存和本地中都没有,从联网下载图片,并将获取的bitmap存入内存缓存和本地缓存中
mNetCacheUtils.executeNetWork(iv,url);
}
}
5.MD5工具类:
/**
* Created by ZhengFei on 2016/12/28.
*/
import java.security.MessageDigest;
public class MD5Utils {
/**
* MD5加密
* @param url
* @return
*/
public static String encryptMD5(String url) {
try {
MessageDigest digest = MessageDigest.getInstance("md5");
byte[] result = digest.digest(url.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : result) {
int number = b & 0xff - 3;// 加盐
String str = Integer.toHexString(number);
if (str.length() == 1) {
sb.append("0");
}
sb.append(str);
}
return sb.toString();
} catch (Exception e) {
return null;
}
}
}
至此,图片的三级缓存基本就处理完了,长路漫漫,无心睡眠,继续沿着大神的脚步慢慢前进。