安卓快取之LruCache及設計(非同步+快取)圖片載入器LruCacheImageLoader
一、LruCache
LruCache是一套記憶體快取的解決方案,演算法基於LRU。
LRU:Least Recently Used(近期最少使用)。LruCache基於LRU演算法的快取策略。
LruCache是一個泛型類,其以強引用的方式儲存外界的快取物件。當記憶體快取達到設定的最大值時,則將記憶體快取中近期最少使用的物件移除,有效的避免了OOM的出現。
一、LruCache的基本使用
LruCache一般使用來快取圖片,下面以快取Bitmap為例
建立一個key為String型別,value為Bitmap型別的LruCache
private LruCache<String, Bitmap> mCache;
初始化LruCache,並指定LruCache的快取大小
//獲取應用程式最大可用記憶體 int maxMemory = (int) Runtime.getRuntime().maxMemory(); //指定快取大小 int cacheSize = maxMemory / 8; mCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { //返回Bitmap的實際大小 (單位應與maxMemory一致) return value.getByteCount(); } };
獲取快取
mCache.get(key);
新增快取
mCache.put(key,value);
移除某個鍵的快取
mCache.remove(key);
清空快取
mCache.evictAll()
二、使用LruCache封裝圖片載入快取器
根據LruCache,我們可以將它封裝在LruCacheImageLoader來對圖片進行快取。設計模式為使用單例模式來設計:
在Logcat中列印非同步任務個數:
package com.cxmscb.cxm.cacheproject;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;
import android.widget.ListView;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Set;
/**
* Created by cxm on 2016/8/25.
*/
public class LruCacheImageLoader {
private LruCache<String,Bitmap> mCache;
// 儲存非同步任務的集合
private Set<LruCacheAsyncTask> mTaskSet;
/*ImageLoader的單例*/
private static LruCacheImageLoader mImageLoader;
private Context mContext;
private LruCacheImageLoader(Context context){
this.mContext = context;
mTaskSet = new HashSet<>();
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
// 初始化LruCache
mCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
// 獲取LruCacheImageLoader的例項(帶同步鎖)
public static LruCacheImageLoader getInstance(Context context){
if(mImageLoader==null){
synchronized (LruCacheImageLoader.class){
if(mImageLoader==null)
mImageLoader = new LruCacheImageLoader(context);
}
}
return mImageLoader;
}
// 根據key值獲取快取中的圖片
private Bitmap getBitmapFromMemory(String url){
return mCache.get(url);
}
//將一張圖片儲存到LruCache中。
private void putBitmapToMemory(String url, Bitmap bitmap) {
if (getBitmapFromMemory(url) == null) {
mCache.put(url, bitmap);
}
}
/*------------以上的LruCache的使用-------------*/
/*
* 對外方法:普通地載入圖片到imageView中
*/
public void displayImage(ImageView iv, final String url) {
//從快取中取出圖片
Bitmap bitmap = getBitmapFromMemory(url);
//如果快取中沒有,先設為預設圖片
if (bitmap == null) {
LruCacheAsyncTask task = new LruCacheAsyncTask(iv);
task.execute(url);
mTaskSet.add(task);
} else {
//如果快取中有 直接設定
iv.setImageBitmap(bitmap);
}
}
/**
* 對外方法:為listview載入從start到end的所有的Image
*
*/
public void loadTagImageViewInListView(int start, int end, String[] tagUrls, ListView mListView) {
Drawable.ConstantState aConstantState = mContext.getResources().getDrawable(R.drawable.loading).getConstantState();
for (int i = start; i < end; i++) {
String url = tagUrls[i];
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
// 判斷圖片是否載入過,以免新建多個asynctask
if (imageView.getDrawable().getConstantState().equals(aConstantState)) {
displayImage(imageView, url);
}
}
Log.i("num of asynctask"," "+mTaskSet.size());
}
private class LruCacheAsyncTask extends AsyncTask<String,Void,Bitmap>{
private ImageView imageView;
public LruCacheAsyncTask(ImageView imageView){
this.imageView = imageView;
}
@Override
protected Bitmap doInBackground(String... strings) {
Bitmap bitmap = getBitmapFromUrl(strings[0]);
// 將bitmap快取到LruCache中
if(bitmap!=null){
putBitmapToMemory(strings[0],bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
mTaskSet.remove(this);
Log.i("num of asynctask"," "+mTaskSet.size());
}
private Bitmap getBitmapFromUrl(String urlPath) {
Bitmap bitmap = null;
try {
URL url = new URL(urlPath);
URLConnection conn = url.openConnection();
conn.connect();
InputStream in;
in = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(in);
// TODO Auto-generated catch block
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}
/**
* 停止所有當前正在執行的任務
*/
public void cancelAllTask() {
if (mTaskSet != null) {
for (LruCacheAsyncTask task : mTaskSet) {
task.cancel(false);
}
mTaskSet.clear();
Log.i("num of asynctask"," "+mTaskSet.size());
}
}
}
三、 使用LruCacheImageLoader來載入網路圖片:
一、專案效果圖:
首先是ListView上下滑動時不載入圖片,停止滑動才開始載入圖片
在Logcat中檢視載入圖片的非同步任務個數:
滑動時非同步任務個數為0,滑動停止時任務數為ListView中的item可見數,然後遞減,最後為0,再次滑動/停止時一直為0。主要是利用了下面的優化。
二、對ListView載入圖片的優化:
監聽ListView滾動狀態,只有當其靜止的時候才非同步載入網路圖片資料。(第一次進入也載入圖片)
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if(scrollState==SCROLL_STATE_IDLE){
lruCacheImageLoader.loadTagImageViewInListView(mStart,mEnd,urls,listView);
}else {
// 以免產生太多無用的asynctask
lruCacheImageLoader.cancelAllTask();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mStart = firstVisibleItem;
mEnd = firstVisbleItem + visibleItemCount;
if(mFirstIn && visibleItemCount > 0){
lruCacheImageLoader.loadTagImageViewInListView(mStart,mEnd,urls,listView);
mFirstIn = false;
}
}
});
完整專案地址:Github地址
相關文章
- android ListView非同步載入圖片(雙快取)AndroidView非同步快取
- Android使用LruCache、DiskLruCache實現圖片快取+圖片瀑布流Android快取
- PHP快取之Opcode快取PHP快取
- iOS Cell非同步圖片載入優化,快取機制詳解iOS非同步優化快取
- Android圖片快取之Bitmap詳解Android快取
- 快取圖片快取
- 圖片快取快取
- 【構建Android快取模組】(三)Controller & 非同步圖片載入Android快取Controller非同步
- Android圖片快取之Lru演算法Android快取演算法
- Android圖片快取之Glide進階Android快取IDE
- Android圖片快取之初識GlideAndroid快取IDE
- mybatis快取之一級快取(一)MyBatis快取
- mybatis快取之一級快取(二)MyBatis快取
- 開源框架——圖片載入和快取方案總結框架快取
- Glide 4.0.0 下之載入本地快取的圖片IDE快取
- SDWebImage實現圖片展示、快取、清除快取Web快取
- ASP.NET Core - 快取之分散式快取ASP.NET快取分散式
- 億級流量客戶端快取之Http快取與本地快取對比客戶端快取HTTP
- 詳談Picasso圖片快取庫特點及用法快取
- 圖片三級快取及OOM--android快取OOMAndroid
- ASP.NET Core - 快取之記憶體快取(上)ASP.NET快取記憶體
- ASP.NET Core - 快取之記憶體快取(下)ASP.NET快取記憶體
- 快取之美——如何選擇合適的本地快取?快取
- Android 圖片載入快取問題:為什麼你的Glide快取沒有起作用?Android快取IDE
- Android 視訊縮圖的快取機制和非同步載入Android快取非同步
- Vue 全站快取之 keep-alive : 動態移除快取Vue快取Keep-Alive
- 預載入與快取快取
- Android平滑圖片載入和快取庫 Glide 使用詳解Android快取IDE
- Android 平滑圖片載入和快取庫 Glide 使用詳解Android快取IDE
- Flutter快取之mmkvFlutter快取
- React Native圖片快取元件React Native快取元件
- 圖片快取(源於SDK文件)快取
- Android 圖片快取處理Android快取
- android非同步圖片載入三之handler+執行緒池+訊息佇列模式+快取Android非同步執行緒佇列模式快取
- Mybatis延遲載入、快取MyBatis快取
- Vue 全站快取二:如何設計全站快取Vue快取
- 如何設計快取系統:快取穿透,快取擊穿,快取雪崩解決方案分析快取穿透
- Android記憶體快取LruCache原始碼解析Android記憶體快取原始碼