現在android應用中不可避免的要使用圖片,有些圖片是可以變化的,需要每次啓動時從網絡拉取,這種場景在目前市場的應用以及純圖片應用(比如百度美拍)中比較多。
實現圖片緩存也不難,需要有相應的cache策略。這裏我採用 內存(memory)-本地(local)-網絡(Internet) 三層cache機制,其實網絡不算cache。
當根據url向網絡拉取圖片的時候,先從本應用內存中找,如果內存中沒有,再從本地緩存文件中查找,如果緩存文件中也沒有,再從網絡上通過http請求拉取圖 片。在鍵值對(key-value)中,這個圖片緩存的key是圖片url的hash值,value就是bitmap。所以,按照這個邏輯,只要一個 url被下載過,其圖片就被緩存起來了。
三級緩存的比較:
-內存緩存,優先加載,速度最快
-本地緩存,次優先加載,速度快
-網絡緩存,不優先加載,速度慢,浪費流量。
現在我們來看看怎麼去實現這個緩存。
首先是網絡緩存的類:
/**
*
* 從網絡下載圖片
* @lly
*
*/
public class NetCacheUtils {
private LocalCacheUtils mlocalcacheutils;
private MemoryCacheUtils mmemorycacheutils;
public NetCacheUtils(LocalCacheUtils localcacheutils, MemoryCacheUtils memorycacheutils) {
mlocalcacheutils=localcacheutils;
mmemorycacheutils=memorycacheutils;
}
public void getBitmapFromNet(ImageView iv_photo, String url) {
// TODO Auto-generated method stub
BitmapTask bitmaptask=new BitmapTask();
bitmaptask.execute(iv_photo,url);//開啓AsyncTask,參數在doInBackground獲取
}
/*AsyncTask 異步任務即做一些簡單的異步處理 ;是handle與線程池的封裝
* 第一個泛型:參數類型泛型
* 第二個泛型:更新進度泛型
* 第三個泛型:onProgressUpdate的返回結果的泛型
*
*/
class BitmapTask extends AsyncTask<Object, Void, Bitmap>{
private ImageView pic;
private String murl;
/**
* 後臺耗時方法在此執行,子線程
*/
@Override
protected Bitmap doInBackground(Object... params) {
pic = (ImageView) params[0];
murl = (String) params[1];
pic.setTag(murl);//將圖片與url綁定
return downloadBitmap(murl);
}
/**
* 更新進度,主線程
*/
@Override
protected void onProgressUpdate(Void... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
}
/**
* 後臺耗時方法結束之後,在此執行,主線程
*/
@Override
protected void onPostExecute(Bitmap result) {
if(result!=null){
String tag = (String) pic.getTag();
if(tag.equals(murl)){
pic.setImageBitmap(result);
}
}
mlocalcacheutils.setBitmapTolocal(murl, result);
mmemorycacheutils.setBitmapTomemory(murl, result);
System.out.println("從網絡上加載圖片啦");
}
}
/**
*
* 下載圖片
* @return
*/
private Bitmap downloadBitmap(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 inputStream = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
conn.disconnect();
}
return null;
}
}
本地緩存的實現:
public class LocalCacheUtils {
private static final String CACHE_PATH=Environment.getExternalStorageDirectory()
.getAbsolutePath()+"/zhbj_cache_52";
/**
*
* 從本地讀圖片
* @param url
*/
public Bitmap getBitmapFromlocal(String url){
try {
String filename = MD5Encoder.encode(url);
File file=new File(CACHE_PATH, filename);//通過父文件夾與自己的文件名稱來創建一個文件
if(file.exists()){
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
return bitmap;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
*
* 將圖片寫到本地
* @param url
* @param bitmap
*/
public void setBitmapTolocal(String url,Bitmap bitmap){
try {
String filename = MD5Encoder.encode(url);
File file=new File(filename);
File parentFile = file.getParentFile();
if(!parentFile.exists()){//如果文件夾不存在,則創建
file.mkdirs();
}
//將圖片保存到本地
bitmap.compress(CompressFormat.JPEG, 100,
new FileOutputStream(file));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
接下來是內存緩存的實現:
/**
*
* 內存緩存
* @lly
*
*/
public class MemoryCacheUtils {
private HashMap<String, Bitmap> hashlist=new HashMap<String, Bitmap>();
/**
*
* 從內存中讀
* @param url
* @return
*/
public Bitmap getBitmapFrommemory(String url){
Bitmap bitmap = hashlist.get(url);
return bitmap;
}
/**
*
* 寫入內存
* @param url
* @param bitmap
*/
public void setBitmapTomemory(String url,Bitmap bitmap){
hashlist.put(url, bitmap);
}
}
最後是我們自己實現的圖片加載工具:
/**
*
* 圖片加載工具
* @lly
*
*/
public class BitMaputils {
NetCacheUtils netcache;
LocalCacheUtils localcacheutils;
MemoryCacheUtils memorycacheutils;
public BitMaputils(){
memorycacheutils=new MemoryCacheUtils();
localcacheutils=new LocalCacheUtils();
netcache=new NetCacheUtils(localcacheutils,memorycacheutils);
}
Bitmap bitmap =null;
public void display(ImageView iv_photo, String url) {
iv_photo.setImageResource(R.drawable.news_pic_default);//默認圖片,防止圖片的複用
//內存緩存
bitmap= memorycacheutils.getBitmapFrommemory(url);
if(bitmap!=null){
iv_photo.setImageBitmap(bitmap);
System.out.println("從內存中讀取圖片");
return;
}
//本地緩存
bitmap = localcacheutils.getBitmapFromlocal(url);
if(bitmap!=null){
iv_photo.setImageBitmap(bitmap);
memorycacheutils.setBitmapTomemory(url, bitmap);
System.out.println("從本地讀取圖片");
return;//從本地讀取就不需要從網絡讀取了
}
//網絡緩存(第一次)
netcache.getBitmapFromNet(iv_photo,url);
}
}
圖片框架:Android-Universal-Image-Loader 也是採用了三級緩存的思路。