關於圖片的處理,必不可少要用到三級緩存技術。
什么是三級緩存?
三級緩存一般分為內存--文件--網絡三級緩存
內存(手機內存):內存相對於磁盤緩存,速度會快很多,但是缺點就是容量較小,不能存儲大容量數據,且容易被系統回收。LruCache
磁盤(SD卡):相對於內存來說存儲空間很大,能夠存儲較多的數據。DiskLruCache(DiskLruCache是非Google官方編寫,但獲得官方認證的硬盤緩存類,該類沒有限定在Android內,所以理論上Java應用也可以使用DiskLreCache來緩存。)
網絡:服務器端,通過HTTP請求獲得。
具體流程就是,同一張圖片從網絡獲取一次,然后在本地緩存下來,之后加載同一張圖片的時候就從緩存中去加載。
先在內存中查找,找到就進行加載,否則去磁盤查找,找到將圖片添加到內存中,加載顯示,否則從網絡加載,並且緩存到內存和磁盤,並返回。這就是完整的三級緩存過程。
這里需要介紹的是內存緩存一共分為四類:強引用、軟引用、弱引用和虛引用(后面我會再專門整理一篇博客的~)
到這里三級緩存應該大概明白了吧,下面是我寫的實現demo
package simida.yz.com.imagecache.utils;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* Created by Administrator on 2016/12/2.
*/
public class LoadImageCache {
private Context mContext;
//網絡地址
private String mImageUrl;
//磁盤存儲文件名
private String mFileName;
//內存緩存
private LruCache<String, Bitmap> mLruCache;
//磁盤目錄(sd卡的私有cache目錄)
private String mFilePath;
public LoadImageCache(Context context, String url) {
this.mContext = context;
this.mImageUrl = url;
initLruCache();
initFileCache();
}
public void loadBitmap(final ImageView imageView) {
//現在內存中查找
Bitmap bitmap = mLruCache.get(mImageUrl);
if (bitmap != null) {
Log.i("tag", "thumbnail from LruCache ");
imageView.setImageBitmap(bitmap);
return;
} else {
//去磁盤查找
byte[] bytes = loadFromSD();
if (bytes != null) {
//保存縮略圖到cache
saveThumbnailToLruCache(bytes, 500, 300);
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Log.i("tag", "bitmap from sdcard ");
imageView.setImageBitmap(bitmap);
return;
} else {
//網絡加載 使用OKHTTP異步實現
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(mImageUrl).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
byte[] fromNetWork = response.body().bytes();
//保存縮略圖到cache
saveThumbnailToLruCache(fromNetWork, 500, 300);
//保存到sdcard
saveImageToSD(fromNetWork);
final Bitmap bitmap1 = BitmapFactory.decodeByteArray(fromNetWork, 0, fromNetWork.length);
Log.i("tag", "bitmap from network ");
((Activity)mContext).runOnUiThread(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap1);
}
});
}
});
}
}
}
//初始化磁盤緩存目錄
private void initFileCache() {
//獲取文件名稱
mFileName = mImageUrl.substring(mImageUrl.lastIndexOf("/") + 1);
//判斷sd卡是否掛載
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//私有緩存目錄
mFilePath = mContext.getExternalCacheDir().getAbsolutePath();
} else {
Log.i("tag", "sdcard is error! ");
}
}
//初始化內存緩存
private void initLruCache() {
if (mLruCache == null) {
//獲取運行時內存總大小
long maxMemory = Runtime.getRuntime().maxMemory();
//一般設置圖片緩存為手機內存的1/8
mLruCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {
//用來衡量每張圖片的大小,默認返回圖片的數量
@Override
protected int sizeOf(String key, Bitmap value) {
//圖片默認是ARGB_8888格式,每個像素占4個字節
// int values = value.getWidth() * value.getHeight() * 4;
int values = value.getRowBytes() * value.getHeight();
return values;
}
//結合軟引用使用時,會配合這個方法,現在已經基本不用軟引用,不用考慮
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
}
};
}
}
//保存圖片到磁盤
private void saveImageToSD(byte[] image) {
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(new File(mFilePath, mFileName)));
bos.write(image, 0, image.length);
bos.flush();
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//關流
try {
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Log.i("tag", "bitmap to sdcard ");
}
//磁盤查找
private byte[] loadFromSD() {
FileInputStream fis = null;
BufferedInputStream bis = null;
//內存流
ByteArrayOutputStream baos = null;
try {
fis = new FileInputStream(new File(mFilePath, mFileName));
bis = new BufferedInputStream(fis);
baos = new ByteArrayOutputStream();
byte[] bys = new byte[1024 * 8];
int length = 0;
while ((length = bis.read(bys)) != -1) {
baos.write(bys, 0, length);
baos.flush();
}
byte[] bytes = baos.toByteArray();
return bytes;
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
//關流
try {
if (baos != null) {
baos.close();
}
if (bis != null) {
bis.close();
}
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
Log.i("tag", "bitmap from sdcard ");
}
}
//保存圖片的縮略圖到內存緩存,(二次采樣技術)
private void saveThumbnailToLruCache(byte[] image, int thumbWidth, int thumbHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
//只采圖片的邊界
options.inJustDecodeBounds = true;
//獲取原圖的高度和寬度
BitmapFactory.decodeByteArray(image, 0, image.length, options);
int width = options.outWidth;
int height = options.outHeight;
//計算縮略圖寬高與原圖寬高比例,取較大值作為最終縮放比例
int size0 = (int) (thumbWidth / (float) width);
int size1 = (int) (thumbHeight / (float) height);
int size = size0 > size1 ? size0 : size1;
options.inSampleSize = size;
//設置圖片格式
options.inPreferredConfig = Bitmap.Config.RGB_565;
//采圖片全部
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeByteArray(image, 0, image.length, options);
mLruCache.put(mImageUrl, bitmap);
Log.i("tag", "thumbnail to cache ");
}
}
Activity和布局文件都比較簡單,布局中只有一個imageView,實例化上面個工具類,調用loadBitmap方法即可,這里就不貼出來了。
第一次打開時log信息:

關閉后打開log信息:

到這里三級緩存可以看明白了吧。
這只是三級緩存的簡單實現,實際項目中,有許多優秀的第三方框架已經將緩存機制優化的非常棒了,有興趣的朋友可以繼續看我的博客,最近沒有工作,想要好好整理一下以前的東西了~再不整理都忘記啦。。。
