淺談圖片加載的三級緩存
之前被人問及過,圖片的三級緩存是什么啊,來給我講講,圖片三級緩存,好高大尚的名字,聽著挺厲害,應該是很厲害的技術,當時不會啊,也就沒什么了,沒有說出來唄,前一階端用到了BitmapUtils的圖片緩存框架,索性就自己找些知識點來研究一些圖片的三級緩存是什么吧。真所謂是知識你要是不知道,那就真的說不出所以然來,但是當你真正的去了解了,三級緩存也不是那么高端的技術。好了,閑話不多說了,開始圖片的三級緩存原理吧。
什么是圖片的三級緩存
- 1、內存緩存 優先加載,速度最快
- 2、本地緩存 次優先加載 速度稍快
- 3、網絡緩存 最后加載 速度由網絡速度決定(浪費流量)
圖片的三級緩存圖形解釋
這里寫圖片描述
好了,既然知道了什么三級緩存的字面意思了,那么我們就來處理吧。
圖片緩存---內存緩存的原理
MemoryCacheUtils.java:
package com.example.bitmaputils.bitmap;
import android.graphics.Bitmap;
import android.util.Log;
import android.util.LruCache;
/**
* 內存緩存
*/
public class MemoryCacheUtils {
/**
* LinkedHashMap<>(10,0.75f,true);
* <p/>
* 10是最大致 0.75f是加載因子 true是訪問排序 false插入排序
*
*
*/
//private LinkedHashMap<String,Bitmap> mMemoryCache = new LinkedHashMap<>(5,0.75f,true);
private LruCache<String, Bitmap> mLruCache;
public MemoryCacheUtils() {
long maxMemory = Runtime.getRuntime().maxMemory();//最大內存 默認是16兆 運行時候的
mLruCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {
@Override
protected int sizeOf(String key, Bitmap value) {
//int byteCount = value.getByteCount();
//得到圖片字節數
// @return number of bytes between rows of the native bitmap pixels.
int byteCount = value.getRowBytes() * value.getWidth();
return byteCount;
}
};
}
/**
* 從內存中讀取
*
* @param url
*/
public Bitmap getFromMemroy(String url) {
Log.d("MyBitmapUtils", "從內存中加載圖片");
return mLruCache.get(url);
}
/**
* 寫入到內存中
*
* @param url
* @param bitmap
*/
public void setToMemory(String url, Bitmap bitmap) {
mLruCache.put(url, bitmap);
}
}
很簡單吧,在這里我們使用了LruCache(),對圖片進行了內存緩存,這里我們只是稍微進行了處理,在這里我們只是介紹原理,當然了哈,性能,OOM溢出問題我們在這里不作處理。
圖片緩存---本地緩存
package com.example.bitmaputils.bitmap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;
import com.example.bitmaputils.MD5Encoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* 本地緩存
*/
public class SDcardCacheUtils {
/**
* 我們讀取內存的絕對路徑
*/
public static final String CACHE_PATH = Environment
.getExternalStorageDirectory().getAbsolutePath() + "/aixuexi";
/**
* 從本地讀取
* @param url
*/
public Bitmap getFromSd(String url){
String fileName = null;
try {
//得到圖片的url的md5的文件名
fileName = MD5Encoder.encode(url);
} catch (Exception e) {
e.printStackTrace();
}
File file = new File(CACHE_PATH,fileName);
//如果存在,就通過bitmap工廠,返回的bitmap,然后返回bitmap
if (file.exists()){
try {
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
Log.d("MyBitmapUtils", "從本地讀取圖片啊");
return bitmap;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 向本地緩存
*
* @param url 圖片地址
* @param bitmap 圖片
*/
public void savaSd(String url,Bitmap bitmap){
String fileName = null;
try {
//我們對圖片的地址進行MD5加密,作為文件名
fileName = MD5Encoder.encode(url);
} catch (Exception e) {
e.printStackTrace();
}
/**
* 以CACHE_PATH為文件夾 fileName為文件名
*/
File file = new File(CACHE_PATH,fileName);
//我們首先得到他的符文劍
File parentFile = file.getParentFile();
//查看是否存在,如果不存在就創建
if (!parentFile.exists()){
parentFile.mkdirs(); //創建文件夾
}
try {
//將圖片保存到本地
/**
* @param format The format of the compressed image 圖片的保存格式
* @param quality Hint to the compressor, 0-100. 0 meaning compress for
* small size, 100 meaning compress for max quality. Some
* formats, like PNG which is lossless, will ignore the
* quality setting
* 圖片的保存的質量 100最好
* @param stream The outputstream to write the compressed data.
*/
bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
圖片緩存---網絡緩存
NetCacheUtils .java:
package com.example.bitmaputils.bitmap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 網絡緩存工具類
*/
public class NetCacheUtils {
/**
* 圖片
*/
private ImageView mImageView;
/**
* 圖片地址
*/
private String mUrl;
/**
* 本地緩存
*/
private SDcardCacheUtils mDcardCacheUtils;
/**
* 內存緩存
*/
private MemoryCacheUtils mMemoryCacheUtils;
public NetCacheUtils(SDcardCacheUtils dcardCacheUtils, MemoryCacheUtils memoryCacheUtils) {
mDcardCacheUtils = dcardCacheUtils;
mMemoryCacheUtils = memoryCacheUtils;
}
/**
* 從網絡中下載圖片
*
* @param image
* @param url
*/
public void getDataFromNet(ImageView image, String url) {
new MyAsyncTask().execute(image, url); //啟動Asynctask,傳入的參數到對應doInBackground()
}
/**
* 異步下載
* <p/>
* 第一個泛型 : 參數類型 對應doInBackground()
* 第二個泛型 : 更新進度 對應onProgressUpdate()
* 第三個泛型 : 返回結果result 對應onPostExecute
*/
class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> {
/**
* 后臺下載 子線程
*
* @param params
* @return
*/
@Override
protected Bitmap doInBackground(Object... params) {
//拿到傳入的image
mImageView = (ImageView) params[0];
//得到圖片的地址
mUrl = (String) params[1];
//將imageview和url綁定,防止錯亂
mImageView.setTag(mUrl);
Bitmap bitmap = downLoadBitmap(mUrl);
return bitmap;
}
/**
* 進度更新 UI線程
*
* @param values
*/
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
/**
* 回調結果,耗時方法結束后,主線程
*
* @param bitmap
*/
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
//得到圖片的tag值
String url = (String) mImageView.getTag();
//確保圖片設置給了正確的image
if (url.equals(mUrl)) {
mImageView.setImageBitmap(bitmap);
/**
* 當從網絡上下載好之后保存到sdcard中
*/
mDcardCacheUtils.savaSd(mUrl, bitmap);
/**
* 寫入到內存中
*/
mMemoryCacheUtils.setToMemory(mUrl, bitmap);
Log.d("MyBitmapUtils", "我是從網絡緩存中讀取的圖片啊");
}
}
}
}
/**
* 下載圖片
*
* @param url 下載圖片地址
* @return
*/
private Bitmap downLoadBitmap(String url) {
//連接
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url)
.openConnection();
//設置讀取超時
conn.setReadTimeout(5000);
//設置請求方法
conn.setRequestMethod("GET");
//設置連接超時連接
conn.setConnectTimeout(5000);
//連接
conn.connect();
//響應碼
int code = conn.getResponseCode();
if (code == 200) { //請求正確的響應碼是200
//得到響應流
InputStream inputStream = conn.getInputStream();
//得到bitmap對象
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
} catch (IOException e) {
#
e.printStackTrace();
} finally {
conn.disconnect();
}
return null;
}
}
好了,至此,一個簡單的圖片的三級緩存完成了,接下來看看使用吧。
圖片加載工具類
MyBitmapUtils .java:
package com.example.bitmaputils.bitmap;
/**
* Created by 若蘭 on 2016/1/29.
* 一個懂得了編程樂趣的小白,希望自己
* 能夠在這個道路上走的很遠,也希望自己學習到的
* 知識可以幫助更多的人,分享就是學習的一種樂趣
* csdn:http://blog.csdn.net/wuyinlei
*/
import android.graphics.Bitmap;
import android.widget.ImageView;
/**
* 自定義的bitmap工具類
*/
public class MyBitmapUtils {
/**
* 網絡緩存
*/
public NetCacheUtils mNetCacheUtils;
/**
* 本地緩存
*/
public SDcardCacheUtils mSdCacheUtils;
/**
* 內存緩存
*/
public MemoryCacheUtils mMemoryCacheUtils;
public MyBitmapUtils() {
mSdCacheUtils = new SDcardCacheUtils();
mMemoryCacheUtils = new MemoryCacheUtils();
mNetCacheUtils = new NetCacheUtils(mSdCacheUtils, mMemoryCacheUtils);
}
/**
* 展示圖片的方法
*
* @param image
* @param url
*/
public void display(ImageView image, String url) {
//從內存中讀取
Bitmap fromMemroy = mMemoryCacheUtils.getFromMemroy(url);
//如果內存中有的h話就直接返回,從內存中讀取
if (fromMemroy != null) {
image.setImageBitmap(fromMemroy);
return;
}
//從本地SD卡讀取
Bitmap fromSd = mSdCacheUtils.getFromSd(url);
if (fromSd != null) {
image.setImageBitmap(fromSd);
mMemoryCacheUtils.setToMemory(url, fromSd);
return;
}
//從網絡中讀取
mNetCacheUtils.getDataFromNet(image, url);
}
}
使用這個工具類就很簡單了
只需在加載數據的適配器中
//聲明圖片加載工具類
private MyBitmapUtils utils;
public PhotoAdapter() {
//mBitmapUtils = new BitmapUtils(MainActivity.this);
// mBitmapUtils.configDefaultLoadingImage(R.mipmap.defaut);
utils = new MyBitmapUtils();
}
然后在getView()方法中,使用工具類中的display()方法就可以了。簡單吧
utils.display(holder.tvImage,mImageViews[position]);
在這里我們看下圖片加載效果:
這里寫圖片描述
來看下LOGCAT日志:
01-29 11:19:48.127 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:48.395 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:48.687 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:49.282 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從內存中加載圖片
01-29 11:19:49.628 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:50.173 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 我是從網絡緩存中讀取的圖片啊
01-29 11:19:58.630 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從內存中加載圖片
01-29 11:19:58.762 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:59.325 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 我是從網絡緩存中讀取的圖片啊
01-29 11:19:59.624 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:40.897 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:40.923 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從內存中加載圖片
01-29 11:56:41.105 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:41.118 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從內存中加載圖片
01-29 11:56:41.268 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:41.283 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從內存中加載圖片
01-29 11:56:41.431 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:43.837 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從內存中加載圖片
01-29 11:56:44.013 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:46.047 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從內存中加載圖片
01-29 11:56:46.222 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
好了,一個簡單的圖片三級緩存原理就這樣的誕生了,當然正如開始所說,這里只是簡單的原理介紹,像里面用到的AsyncTask異步加載,和LruCache在這里只是使用,在以后的篇章中會對這兩個進行介紹的,好了,由于代碼展示的代碼量太多,這里我就不在上傳怎么顯示圖片的源碼了,下面我會提供github源碼地址,可以找到我的這個demo的源碼。如果有問題,或者獨特的見解,咱們可以討論哦
- 該項目github地址:https://github.com/wuyinlei/MyBitmapUtils