開發(fā)Android程序,一般情況下都會有兩個操作,圖片的異步加載與緩存,而圖片的異步加載大都是從網(wǎng)絡(luò)讀取圖片(還有生成本地圖片縮略圖等操作),為了減少網(wǎng)絡(luò)操作,加快圖片加載速度就需要對圖片進(jìn)行緩存,所以網(wǎng)上的好多圖片異步加載方法都是與圖片的緩存緊密關(guān)聯(lián)的。但也有可能用戶已經(jīng)有了緩存的相關(guān)類庫,這樣使用起來就會有點麻煩。
最近一段處理跟圖片相關(guān)的問題,本來是自己寫的圖片加載,不過有些狀態(tài)的控制還是比較煩人的,比如ListView滾動時ImageView的重用,所以本著偷懶與充分利用現(xiàn)有資源的態(tài)度去網(wǎng)上搜羅圖片異步加載的代碼,最終在GreenDroid UI庫中找到一個,其中有個AsyncImageView的自定義View用于異步加載圖片,不過也像網(wǎng)上的大多數(shù)圖片異步加載方法一樣,是跟圖片的緩存關(guān)聯(lián)在一起的,不過只是很簡單的內(nèi)存緩存,無文件緩存。圖片的加載方法也如其他的一樣是寫死了的,這就限制了其使用范圍,只可通過InputStream來decode圖片,而像生成縮略圖或其他一些圖片處理的異步處理就無法用途。修改現(xiàn)有類庫總比自己從頭寫來的簡單,于是稍微修改了下AsyncImageView,使其可以自定義緩存與圖片加載方法,對于AsyncImageView只有一點點的修改,大都是別人源碼。
1. 核心類
ImageLoader:圖片加載核心類,內(nèi)部使用線程池加載圖片
ImageRequest:表示一個圖片加載的請求
AsyncImageView:自定義的圖片異步加載View
LoadMethod:自定義圖片加載方法的接口,可以通過實現(xiàn)此接口來自定義圖片的加載方法
CacheCallback:緩存接口,可以通過實現(xiàn)此接口實現(xiàn)對緩存的讀寫
AsyncImageView.OnImageViewLoadListener:圖片加載狀態(tài)監(jiān)聽(開始,失敗,結(jié)束)
2。圖片加載方法
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
final Handler h = mHandler;
Bitmap bitmap = null;
Throwable throwable = null;
h.sendMessage(Message.obtain(h, ON_START));
try {
if (TextUtils.isEmpty(mUrl)) {
throw new Exception("The given URL cannot be null or empty");
}
// 如果自定義了加載方法,則用自定義的方法
if (mLoadMethod != null) {
bitmap = mLoadMethod.load(mUrl);
} else {
InputStream inputStream = null;
// Asset
if (mUrl.startsWith("file:///android_asset/")) {
inputStream = sAssetManager.open(mUrl.replaceFirst(
"file:///android_asset/", ""));
}
// File
else if (mUrl.startsWith("file:///") || mUrl.startsWith("/")) {
if (mUrl.startsWith("file:///"))
mUrl = mUrl.replaceFirst("file:///", "/");
inputStream = new FileInputStream(mUrl);
}
// NetWork
else {
// 在用URL類加載圖片時,發(fā)現(xiàn)有的機型上面通過URL類獲得的InputStream解析獲得的圖片總是null,故使用HttpClient
HttpGet httpRequest = new HttpGet(mUrl);
HttpClient httpclient = new DefaultHttpClient();
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, 5000);
httpRequest.setParams(httpParams);
HttpResponse response = (HttpResponse)httpclient.execute(httpRequest);
HttpEntity entity = response.getEntity();
BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
InputStream instream = bufHttpEntity.getContent();
BufferedInputStream bi = new BufferedInputStream(instream);
inputStream = bi;
}
// 雖然AsyncImageView中有設(shè)置BitmapFactory.Options的方法,但一般情況下都未知圖片的大小,也就無法計算相應(yīng)的inSampleSize,
// 也就無法設(shè)置相應(yīng)的BitmapFactory.Options,所以一般情況下還是根據(jù)自己的需要自定義LoadMethod為好
bitmap = BitmapFactory.decodeStream(inputStream, null,
(mOptions == null) ? sDefaultOptions : mOptions);
inputStream.close();
}
if (mBitmapProcessor != null && bitmap != null) {
final Bitmap processedBitmap = mBitmapProcessor.processImage(bitmap);
if (processedBitmap != null) {
bitmap.recycle();
bitmap = processedBitmap;
}
}
} catch (Exception e) {
Log.e(LOG_TAG, "Error while fetching image", e);
throwable = e;
}
if (bitmap == null) {
if (throwable == null) {
throwable = new Exception("Skia image decoding failed");
}
h.sendMessage(Message.obtain(h, ON_FAIL, throwable));
} else {
h.sendMessage(Message.obtain(h, ON_END, bitmap));
if (mCache != null) {
mCache.writeCache(TextUtils.isEmpty(mCacheKey) ? mUrl : mCacheKey, bitmap);
}
}
}
如果自定義了LoadMethod,會調(diào)用相應(yīng)的方法加載圖片,如果沒有自定義,會使用默認(rèn)的加載方法,可以加載本地圖片,Asset圖片與網(wǎng)絡(luò)圖片,GreenDroid的源碼中加載網(wǎng)絡(luò)圖片是用的URL的,但我們以前在加載網(wǎng)絡(luò)圖片時遇到一個問題,有的機型通過URL類獲得的ImputStream解析圖片總是返回null,所以就改為了HttpClient。
3。使用方法
通過AsyncImageView的setPath方法來加載圖片,setPath有3個重載方法:
public void setPath(String path)
public void setPath(String path, LoadMethod loadMethod)
public void setPath(String path, LoadMethod loadMethod, String cacheKey)
第一個參數(shù)指定要加載的圖片的路徑,第二個參數(shù)為自定義的圖片加載方法,若不指定則用默認(rèn)的。
至于加第三個參數(shù),是做緩存用的,一般要加載的圖片的路徑都是唯一的,所以一般用第一個參數(shù)來做為緩存的Key就行了,但也有特殊情況,比如讀取局域網(wǎng)中的圖片,一般都是自動獲取IP,所以根據(jù)圖片路徑做為緩存的Key可能是不合適的,所以就需要根據(jù)需要手動指定用來作為緩存的Key。
/**
* 設(shè)置要加載的圖片的路徑, 可為網(wǎng)絡(luò)路徑, Asset文件路徑(file:///android_asset), 本地圖片路徑(file:///或/)
*
* @param path 要加載的圖片的路徑, 若為null則加載默認(rèn)圖片
* @param loadMethod 自定義的圖片加載的方法, 可以null, 使用默認(rèn)的加載方法
* @param cacheKey 緩存key
*/
public void setPath(String path, LoadMethod loadMethod, String cacheKey) {
// Check the url has changed
if (mBitmap != null && path != null && path.equals(mUrl)) { // TODO mBitmap != null necessary?
return;
}
stopLoading();
mUrl = path;
mCacheKey = cacheKey;
mLoadMethod = loadMethod;
// Setting the url to an empty string force the displayed image to the
// default image
if (TextUtils.isEmpty(mUrl)) {
mBitmap = null;
setDefaultImage();
} else {
if (!mPaused) {
reload();
} else {
// We're paused: let's look in a synchronous and efficient cache
// prior using the default image.
mBitmap = readCache(); // TODO 可能會耗時間
if (mBitmap != null) {
setImageBitmap(mBitmap);
} else {
setDefaultImage();
}
}
}
}
public void reload(boolean force) {
if (mRequest == null && mUrl != null) {
// Prior downloading the image ... let's look in a cache !
mBitmap = null;
if (!force) {
// This may take a long time.
mBitmap = readCache();
}
if (mBitmap != null) {
setImageBitmap(mBitmap);
return;
}
setDefaultImage();
mRequest = new ImageRequest(mUrl, this, mImageProcessor, mOptions, mCacheKey);
mRequest.load(getContext(), mLoadMethod);
if (ImageLoader.getInstance() != null && ImageLoader.getInstance().getCache() == null) {
ImageLoader.getInstance().setCache(mCache);
}
}
readCache()用于讀取緩存,代碼如下:
private Bitmap readCache() { if (mCache != null) return mCache.readCache(TextUtils.isEmpty(mCacheKey) ? mUrl : mCacheKey); return null; }
其中的mCache由用戶能過setCacheCallback(CacheCallback callback)設(shè)置用戶自定義的緩存方法,由此將圖片的加載與緩存分離開,使用戶可以使用現(xiàn)有的緩存實現(xiàn)。如要用戶指定了緩存Key就使用用戶指定的Key,否則就用圖片的路徑作Key。
4.AsyncImageView中的其他重要方法
reload([boolean force]):重新加載
stopLoading():停止加載,如果當(dāng)前正在加載則沒有效果,如果加載任務(wù)在加載線程池隊列中則取消。
setDefaultImage...()類方法:設(shè)置默認(rèn)圖片。
setPause(boolean pause):是否加載圖處