手寫圖片快取框架 ImageLoader
阿新 • • 發佈:2019-01-05
圖片快取是App開發中最常見的,本篇博文給大家帶來自己手寫的圖片快取框,大致的思路很簡單,首先從記憶體中獲取圖片,如果記憶體中沒有,就從手機本地進行獲取,如果還沒有,就從網路訪問進行獲取。
所以,我們在ImageLoader中只需要暴露一個方法loadImage(),外部只需要呼叫這個方法就可以完成圖片快取的所以邏輯
//載入圖片到對應的控制元件
public void loadImage(String key, ImageView view) {
synchronized (view) {
this.imageView = view;
//檢查快取裡是否有
Bitmap bitmap = getFromCache(key);
if (bitmap != null) {
//快取存在,直接顯示
view.setImageBitmap(bitmap);
} else {
//網路進行下載
/*view.setBackgroundDrawable(drawable);*/
view.setBackgroundDrawable(new ColorDrawable(Color.GRAY));
ImageAsycTask task = new ImageAsycTask(view);
task.execute(key);
}
}
}
這裡,我將從記憶體中和本地獲取圖片的邏輯都統一放在getFromCache()方法中,這裡值得一提的是,當記憶體中沒有,本地有該圖片的時候,還會將這個圖片放入LinkedHashMap中,讓這個圖片在LinkedHashMap中處於最新的位置,不至於被回收。
private Bitmap getFromCache(String key) {
//檢查記憶體軟引用
synchronized (firstHashMap) {
if (firstHashMap.get(key) != null) {
Bitmap bitmap = firstHashMap.get(key).get();
if (bitmap != null) {
//更新一下,因為Lru演算法會預設清除最老的選項
firstHashMap.put(key, new SoftReference<Bitmap>(bitmap));
return bitmap;
}
}
}
//檢查磁碟
Bitmap bitmap = getFromLocal(key);
if (bitmap != null) {
//更新一下,因為Lru演算法會預設清除最老的選項
firstHashMap.put(key, new SoftReference<Bitmap>(bitmap));
return bitmap;
}
return null;
}
在記憶體中,我使用了一個LinkedHashMap
private static LinkedHashMap<String, SoftReference<Bitmap>> firstHashMap = new LinkedHashMap<String, SoftReference<Bitmap>>(MAX_LENGTH) {
@Override
protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
if (this.size() > MAX_LENGTH) {
//返回true,表示移除最老的
return true;
}
//往磁碟進行新增
diskCache(eldest.getKey(), eldest.getValue());
return false;
}
};
這裡內部的removeEldestEntry()方法內部如果返回true,會預設移除掉最舊的一個成員,返回false表示不移除,同時還會把圖片放入到手機本地中,這個邏輯通過diskCache()方法實現的,這裡圖片在本地中名字使用md5加密後的名字
// 把圖片快取到本地磁碟
private static void diskCache(String key, SoftReference<Bitmap> value) {
//訊息摘要演算法
Bitmap bitmap;
FileOutputStream os = null;
try {
String fileName = MD5Utils.md5(key, "utf-8");
String path = mContext.getCacheDir().getAbsolutePath() + File.separator + fileName;
os = new FileOutputStream(new File(path));
if (value.get() != null) {
value.get().compress(Bitmap.CompressFormat.JPEG, 80, os);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如果本地快取中沒有,會通過getFromLocal(key)方法,從手機本地中進行獲取
//檢查sd卡里是否有
private Bitmap getFromLocal(String key) {
InputStream is = null;
try {
String filname = MD5Utils.md5(key, "utf-8");
if (filname == null) {
return null;
} else {
String path = mContext.getCacheDir().getAbsolutePath() + File.separator + filname;
is = new FileInputStream(new File(path));
Bitmap bitmap = BitmapFactory.decodeStream(is);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如果本地和記憶體都沒有的話,那麼就從網路進行獲取,這裡使用了AsyncTask
class ImageAsycTask extends AsyncTask<String, Void, Bitmap> {
private ImageView imagView;
private String key;
public ImageAsycTask(ImageView imageView) {
this.imagView = imageView;
}
@Override
protected Bitmap doInBackground(String... strings) {
this.key = strings[0];
Log.i(TAG,key);
Bitmap bitmap = downLoad(key);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (bitmap != null) {
addCache(key, bitmap);
/*Log.i("11",bitmap.toString());*/
imagView.setImageBitmap(bitmap);
}
}
}
其中downLoad()方法就是訪問網路獲取圖片的方法
private Bitmap downLoad(String key) {
final Bitmap[] bitmap = new Bitmap[1];
mHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(key).build();
mHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i("TAG", "網路訪問失敗了");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = response.body().byteStream();
bitmap[0] = BitmapFactory.decodeStream(is);
Log.d("okHttp", bitmap[0].toString());
}
});
return bitmap[0];
}
圖片下載完成之後,我們會將其讀寫到記憶體中,並顯示在view上,這個view是通過AsyncTask的建構函式傳進來的
private void addCache(String key, Bitmap bitmap) {
if (bitmap != null) {
synchronized (firstHashMap) {
firstHashMap.put(key, new SoftReference<Bitmap>(bitmap));
}
}
}
這樣這個圖片快取框架就寫好了,我們就單純的在MainActivity中訪問網路進行顯示來驗證我們的框架,佈局太簡單就不貼了
public class MainActivity extends AppCompatActivity {
String url = "http://7mno4h.com2.z0.glb.qiniucdn.com/560bd9b6Nc4b5cbfe.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = (ImageView) findViewById(R.id.image);
ImageLoader imageLoader = ImageLoader.getmInstance(this);
imageLoader.loadImage(url,imageView);
}
}
執行專案,效果如下: