android中圖片的三級緩衝cache策略(記憶體/檔案/網路)

來源:互聯網
上載者:User

1.簡介
現在android應用中不可避免的要使用圖片,有些圖片是可以變化的,需要每次啟動時從網路拉取,這種情境在有廣告位的應用以及純圖片應用(比如百度美拍)中比較多。

現在有一個問題:假如每次啟動的時候都從網路拉取圖片的話,勢必會消耗很多流量。在當前的狀況下,對於非wifi使用者來說,流量還是很貴的,一個很耗流量的應用,其使用者數量級肯定要受到影響。當然,我想,向百度美拍這樣的應用,必然也有其內部的圖片緩衝策略。總之,圖片緩衝是很重要而且是必須的。

2.圖片緩衝的原理
實現圖片緩衝也不難,需要有相應的cache策略。這裡我採用 記憶體-檔案-網路 三層cache機制,其中記憶體緩衝包括強引用緩衝和軟引用緩衝(SoftReference),其實網路不算cache,這裡姑且也把它划到緩衝的階層中。當根據url向網路拉取圖片的時候,先從記憶體中找,如果記憶體中沒有,再從快取檔案中尋找,如果快取檔案中也沒有,再從網路上通過http請求拉取圖片。在索引值對(key-value)中,這個圖片緩衝的key是圖片url的hash值,value就是bitmap。所以,按照這個邏輯,只要一個url被下載過,其圖片就被緩衝起來了。

關於Java中對象的軟引用(SoftReference),如果一個對象具有軟引用,記憶體空間足夠,垃 圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些對象的記憶體。只要記憶體回收行程沒有回收它,該對象就可以被程式使用。軟引用可用來實現記憶體敏感的高 速緩衝。使用軟引用能防止記憶體泄露,增強程式的健壯性。

從代碼上來說,採用一個ImageManager來負責圖片的管理和緩衝,函數介面為public void loadBitmap(String url, Handler handler) ;其中url為要下載的圖片地址,handler為圖片下載成功後的回調,在handler中處理message,而message中包含了圖片的資訊以及bitmap對象。ImageManager中使用的ImageMemoryCache(記憶體緩衝)、ImageFileCache(檔案快取)以及LruCache(最近最久未使用緩衝)會在後續文章中介紹。

3.代碼ImageManager.java 複製代碼 代碼如下:/*
* 圖片管理
* 非同步擷取圖片,直接調用loadImage()函數,該函數自己判斷是從緩衝還是網路載入
* 同步擷取圖片,直接調用getBitmap()函數,該函數自己判斷是從緩衝還是網路載入
* 僅從本地擷取圖片,調用getBitmapFromNative()
* 僅從網路載入圖片,調用getBitmapFromHttp()
*
*/
public class ImageManager implements IManager
{
private final static String TAG = "ImageManager";

private ImageMemoryCache imageMemoryCache; //記憶體緩衝

private ImageFileCache imageFileCache; //檔案快取

//正在下載的image列表
public static HashMap<String, Handler> ongoingTaskMap = new HashMap<String, Handler>();

//等待下載的image列表
public static HashMap<String, Handler> waitingTaskMap = new HashMap<String, Handler>();

//同時下載圖片的線程個數
final static int MAX_DOWNLOAD_IMAGE_THREAD = 4;

private final Handler downloadStatusHandler = new Handler(){
public void handleMessage(Message msg)
{
startDownloadNext();
}
};

public ImageManager()
{
imageMemoryCache = new ImageMemoryCache();
imageFileCache = new ImageFileCache();
}

/**
* 擷取圖片,多線程的入口
*/
public void loadBitmap(String url, Handler handler)
{
//先從記憶體緩衝中擷取,取到直接載入
Bitmap bitmap = getBitmapFromNative(url);
if (bitmap != null)
{
Logger.d(TAG, "loadBitmap:loaded from native");
Message msg = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("url", url);
msg.obj = bitmap;
msg.setData(bundle);
handler.sendMessage(msg);
}
else
{
Logger.d(TAG, "loadBitmap:will load by network");
downloadBmpOnNewThread(url, handler);
}
}
/**
* 新起線程下載圖片
*/
private void downloadBmpOnNewThread(final String url, final Handler handler)
{
Logger.d(TAG, "ongoingTaskMap'size=" + ongoingTaskMap.size());

if (ongoingTaskMap.size() >= MAX_DOWNLOAD_IMAGE_THREAD)
{
synchronized (waitingTaskMap)
{
waitingTaskMap.put(url, handler);
}
}
else
{
synchronized (ongoingTaskMap)
{
ongoingTaskMap.put(url, handler);
}
new Thread()
{
public void run()
{
Bitmap bmp = getBitmapFromHttp(url);
// 不論下載是否成功,都從下載隊列中移除,再由商務邏輯判斷是否重新下載
// 下載圖片使用了httpClientRequest,本身已經帶了重連機制
synchronized (ongoingTaskMap)
{
ongoingTaskMap.remove(url);
}

if(downloadStatusHandler != null)
{
downloadStatusHandler.sendEmptyMessage(0);

}
Message msg = Message.obtain();
msg.obj = bmp;
Bundle bundle = new Bundle();
bundle.putString("url", url);
msg.setData(bundle);

if(handler != null)
{
handler.sendMessage(msg);
}
}
}.start();
}
}
/**
* 依次從記憶體,快取檔案,網路上載入單個bitmap,不考慮線程的問題
*/
public Bitmap getBitmap(String url)
{
// 從記憶體緩衝中擷取圖片
Bitmap bitmap = imageMemoryCache.getBitmapFromMemory(url);
if (bitmap == null)
{
// 檔案快取中擷取
bitmap = imageFileCache.getImageFromFile(url);
if (bitmap != null)
{
// 添加到記憶體緩衝
imageMemoryCache.addBitmapToMemory(url, bitmap);
}
else
{
// 從網路擷取
bitmap = getBitmapFromHttp(url);
}
}
return bitmap;
}

/**
* 從記憶體或者快取檔案中擷取bitmap
*/
public Bitmap getBitmapFromNative(String url)
{
Bitmap bitmap = null;
bitmap = imageMemoryCache.getBitmapFromMemory(url);

if(bitmap == null)
{
bitmap = imageFileCache.getImageFromFile(url);
if(bitmap != null)
{
// 添加到記憶體緩衝
imageMemoryCache.addBitmapToMemory(url, bitmap);
}
}
return bitmap;
}

/**
* 通過網路下載圖片,與線程無關
*/
public Bitmap getBitmapFromHttp(String url)
{
Bitmap bmp = null;

try
{
byte[] tmpPicByte = getImageBytes(url);

if (tmpPicByte != null)
{
bmp = BitmapFactory.decodeByteArray(tmpPicByte, 0,
tmpPicByte.length);
}
tmpPicByte = null;
}
catch(Exception e)
{
e.printStackTrace();
}

if(bmp != null)
{
// 添加到檔案快取
imageFileCache.saveBitmapToFile(bmp, url);
// 添加到記憶體緩衝
imageMemoryCache.addBitmapToMemory(url, bmp);
}
return bmp;
}

/**
* 下載連結的圖片資源
*
* @param url
*
* @return 圖片
*/
public byte[] getImageBytes(String url)
{
byte[] pic = null;
if (url != null && !"".equals(url))
{
Requester request = RequesterFactory.getRequester(
Requester.REQUEST_REMOTE, RequesterFactory.IMPL_HC);
// 執行請求
MyResponse myResponse = null;
MyRequest mMyRequest;
mMyRequest = new MyRequest();
mMyRequest.setUrl(url);
mMyRequest.addHeader(HttpHeader.REQ.ACCEPT_ENCODING, "identity");
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
myResponse = request.execute(mMyRequest);
is = myResponse.getInputStream().getImpl();
baos = new ByteArrayOutputStream();
byte[] b = new byte[512];
int len = 0;
while ((len = is.read(b)) != -1)
{
baos.write(b, 0, len);
baos.flush();
}
pic = baos.toByteArray();
Logger.d(TAG, "icon bytes.length=" + pic.length);
}
catch (Exception e3)
{
e3.printStackTrace();
try
{
Logger.e(TAG,
"download shortcut icon faild and responsecode="
+ myResponse.getStatusCode());
}
catch (Exception e4)
{
e4.printStackTrace();
}
}
finally
{
try
{
if (is != null)
{
is.close();
is = null;
}
}
catch (Exception e2)
{
e2.printStackTrace();
}
try
{
if (baos != null)
{
baos.close();
baos = null;
}
}
catch (Exception e2)
{
e2.printStackTrace();
}
try
{
request.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
return pic;
}

/**
* 取出等待隊列第一個任務,開始下載
*/
private void startDownloadNext()
{
synchronized(waitingTaskMap)
{
Logger.d(TAG, "begin start next");
Iterator iter = waitingTaskMap.entrySet().iterator();

while (iter.hasNext())
{

Map.Entry entry = (Map.Entry) iter.next();
Logger.d(TAG, "WaitingTaskMap isn't null,url=" + (String)entry.getKey());

if(entry != null)
{
waitingTaskMap.remove(entry.getKey());
downloadBmpOnNewThread((String)entry.getKey(), (Handler)entry.getValue());
}
break;
}
}
}

public String startDownloadNext_ForUnitTest()
{
String urlString = null;
synchronized(waitingTaskMap)
{
Logger.d(TAG, "begin start next");
Iterator iter = waitingTaskMap.entrySet().iterator();

while (iter.hasNext())
{
Map.Entry entry = (Map.Entry) iter.next();
urlString = (String)entry.getKey();
waitingTaskMap.remove(entry.getKey());
break;
}
}
return urlString;
}

/**
* 圖片變為圓角
* @param bitmap:傳入的bitmap
* @param pixels:圓角的度數,值越大,圓角越大
* @return bitmap:加入圓角的bitmap
*/
public static Bitmap toRoundCorner(Bitmap bitmap, int pixels)
{
if(bitmap == null)
return null;
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
final float roundPx = pixels;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}

public byte managerId()
{
return IMAGE_ID;
}
}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.