圖片三級緩存
- 內存緩存,最優先從內存加載,速度快+無流量
- 本地緩存,其次從本地加載,速度快+無流量
- 網絡緩存,最後才從網絡加載,速度慢+耗流量
內存溢出
android默認給每個APP只分配16M內存(根據真機有相關浮動)
java引用方式
- 強引用 Person a = new Person();
- 軟引用 SoftReference 較弱
- 弱引用 WeakReference 其次弱
- 虛引用 PhantomReferences 引用最弱
當內存不夠時,垃圾回收器除了回收一些沒有引用的對象之外,也會回收引用不強的對象。
寫圖片的三級緩存
- 網絡緩存類使用AsyncTask異步類處理,重點是內存緩存類,內存緩存中不能用HashMap類來寫, 因爲這樣會涉及內存的溢出(oom)的問題。同時用軟引用的話由於API level 9 之後垃圾回收機制對軟引用對象的回收率更高了,所以已經不適合用軟引用, 重用效果不好。但是可以用LruCache類 這用最近最久未使用方法來回收對象。更適合於對緩存的書寫。
- 其實最適合的是用XUtils類,別人寫的已經很好了!
首先是要寫一個緩存類:
public class MyBitmapUtils {
// 網絡緩存工具類
private NetCacheUtils mNetUtils;
// 本地緩存工具類
private LocalCacheUtils mLocalUtils;
// 內存緩存工具類
private MemoryCacheUtils mMemoryUtils;
public MyBitmapUtils() {
mMemoryUtils = new MemoryCacheUtils();
mLocalUtils = new LocalCacheUtils();
mNetUtils = new NetCacheUtils(mLocalUtils, mMemoryUtils);
}
public void display(ImageView imageView, String url) {
// 設置默認加載圖片
imageView.setImageResource(R.drawable.news_pic_default);
// 先從內存緩存加載
Bitmap bitmap = mMemoryUtils.getBitmapFromMemory(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
System.out.println("從內存讀取圖片啦...");
return;
}
// 再從本地緩存加載
bitmap = mLocalUtils.getBitmapFromLocal(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
System.out.println("從本地讀取圖片啦...");
// 給內存設置圖片
mMemoryUtils.setBitmapToMemory(url, bitmap);
return;
}
// 從網絡緩存加載
mNetUtils.getBitmapFromNet(imageView, url);
}
}
網絡緩存類:
public class NetCacheUtils {
private LocalCacheUtils mLocalUtils;
private MemoryCacheUtils mMemoryUtils;
public NetCacheUtils(LocalCacheUtils localUtils,
MemoryCacheUtils memoryUtils) {
mLocalUtils = localUtils;
mMemoryUtils = memoryUtils;
}
public void getBitmapFromNet(ImageView imageView, String url) {
BitmapTask task = new BitmapTask();
task.execute(imageView, url);
}
/**
* AsyncTask是線程池+handler的封裝 第一個泛型: 傳參的參數類型類型(和doInBackground一致) 第二個泛型:
* 更新進度的參數類型(和onProgressUpdate一致) 第三個泛型: 返回結果的參數類型(和onPostExecute一致,
* 和doInBackground返回類型一致)
*/
class BitmapTask extends AsyncTask<Object, Integer, Bitmap> {
private ImageView mImageView;
private String url;
// 主線程運行, 預加載
@Override
protected void onPreExecute() {
super.onPreExecute();
}
// 子線程運行, 異步加載邏輯在此方法中處理
@Override
protected Bitmap doInBackground(Object... params) {
mImageView = (ImageView) params[0];
url = (String) params[1];
mImageView.setTag(url);// 將imageView和url綁定在一起
// publishProgress(values)//通知進度
// 下載圖片
return download(url);
}
// 主線程運行, 更新進度
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
// 主線程運行, 更新主界面
@Override
protected void onPostExecute(Bitmap result) {
if (result != null) {
// 判斷當前圖片是否就是imageView要的圖片, 防止listview重用導致的圖片錯亂的情況出現
String bindUrl = (String) mImageView.getTag();
if (bindUrl.equals(url)) {
// 給imageView設置圖片
mImageView.setImageBitmap(result);
System.out.println("網絡下載圖片成功!");
// 將圖片保存在本地
mLocalUtils.setBitmapToLocal(result, url);
// 將圖片保存在內存
mMemoryUtils.setBitmapToMemory(url, result);
}
}
}
}
/**
* 下載圖片
*
* @param url
*/
public Bitmap download(String url) {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) (new URL(url).openConnection());
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
conn.connect();
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
InputStream in = conn.getInputStream();
// 將流轉化爲bitmap對象
Bitmap bitmap = BitmapFactory.decodeStream(in);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();
}
}
return null;
}
}
本地緩存類:
public class LocalCacheUtils {
// 圖片緩存的文件夾
public static final String DIR_PATH = Environment
.getExternalStorageDirectory().getAbsolutePath()
+ "/bitmap_cache66";
public Bitmap getBitmapFromLocal(String url) {
try {
File file = new File(DIR_PATH, MD5Encoder.encode(url));
if (file.exists()) {
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
file));
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void setBitmapToLocal(Bitmap bitmap, String url) {
File dirFile = new File(DIR_PATH);
// 創建文件夾
if (!dirFile.exists() || !dirFile.isDirectory()) {
dirFile.mkdirs();
}
try {
File file = new File(DIR_PATH, MD5Encoder.encode(url));
// 將圖片壓縮保存在本地,參1:壓縮格式;參2:壓縮質量(0-100);參3:輸出流
bitmap.compress(CompressFormat.JPEG, 100,
new FileOutputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
}
}
內存緩存類:
public class MemoryCacheUtils {
// private HashMap<String, SoftReference<Bitmap>> mMemroyCache = new
// HashMap<String, SoftReference<Bitmap>>();
// Android 2.3 (API Level
// 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠,建議用LruCache
private LruCache<String, Bitmap> mCache;
public MemoryCacheUtils() {
int maxMemory = (int) Runtime.getRuntime().maxMemory();// 獲取虛擬機分配的最大內存
// 16M
// LRU 最近最少使用, 通過控制內存不要超過最大值(由開發者指定), 來解決內存溢出
mCache = new LruCache<String, Bitmap>(maxMemory / 8) {
@Override
protected int sizeOf(String key, Bitmap value) {
// 計算一個bitmap的大小
int size = value.getRowBytes() * value.getHeight();// 每一行的字節數乘以高度
return size;
}
};
}
public Bitmap getBitmapFromMemory(String url) {
// SoftReference<Bitmap> softReference = mMemroyCache.get(url);
// if (softReference != null) {
// Bitmap bitmap = softReference.get();
// return bitmap;
// }
return mCache.get(url);
}
public void setBitmapToMemory(String url, Bitmap bitmap) {
// SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);
// mMemroyCache.put(url, soft);
mCache.put(url, bitmap);
}
}
MD5相關類:
public class MD5Encoder {
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();
}
}