開發Android程式,一般情況下都會有兩個操作,圖片的非同步載入與緩衝,而圖片的非同步載入大都是從網路讀取圖片(還有產生本地圖片縮圖等操作),為了減少網路操作,加快圖片載入速度就需要對圖片進行緩衝,所以網上的好多圖片非同步載入方法都是與圖片的緩衝緊密關聯的。但也有可能使用者已經有了緩衝的相關類庫,這樣使用起來就會有點麻煩。
最近一段處理跟圖片相關的問題,本來是自己寫的圖片載入,不過有些狀態的控制還是比較煩人的,比如ListView滾動時ImageView的重用,所以本著偷懶與充分利用現有資源的態度去網上搜羅圖片非同步載入的代碼,最終在GreenDroid UI庫中找到一個,其中有個AsyncImageView的自訂View用於非同步載入圖片,不過也像網上的大多數圖片非同步載入方法一樣,是跟圖片的緩衝關聯在一起的,不過只是很簡單的記憶體緩衝,無檔案快取。圖片的載入方法也如其他的一樣是寫死了的,這就限制了其使用範圍,只可通過InputStream來decode圖片,而像產生縮圖或其他一些圖片處理的非同步處理就無法用途。修改現有類庫總比自己從頭寫來的簡單,於是稍微修改了下AsyncImageView,使其可以自訂緩衝與圖片載入方法,對於AsyncImageView只有一點點的修改,大都是別人源碼。
1. 核心類
ImageLoader:圖片載入核心類,內部使用線程池載入圖片
ImageRequest:表示一個圖片載入的請求
AsyncImageView:自訂的圖片非同步載入View
LoadMethod:自訂圖片載入方法的介面,可以通過實現此介面來自訂圖片的載入方法
CacheCallback:緩衝介面,可以通過實現此介面實現對緩衝的讀寫
AsyncImageView.OnImageViewLoadListener:圖片載入狀態監聽(開始,失敗,結束)
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類載入圖片時,發現有的機型上面通過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中有設定BitmapFactory.Options的方法,但一般情況下都未知圖片的大小,也就無法計算相應的inSampleSize, // 也就無法設定相應的BitmapFactory.Options,所以一般情況下還是根據自己的需要自訂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,會調用相應的方法載入圖片,如果沒有自訂,會使用預設的載入方法,可以載入本地圖片,Asset圖片與網狀圖片,GreenDroid的源碼中載入網狀圖片是用的URL的,但我們以前在載入網狀圖片時遇到一個問題,有的機型通過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)
第一個參數指定要載入的圖片的路徑,第二個參數為自訂的圖片載入方法,若不指定則用預設的。
至於加第三個參數,是做緩衝用的,一般要載入的圖片的路徑都是唯一的,所以一般用第一個參數來做為緩衝的Key就行了,但也有特殊情況,比如讀取區域網路中的圖片,一般都是自動擷取IP,所以根據圖片路徑做為緩衝的Key可能是不合適的,所以就需要根據需要手動指定用來作為緩衝的Key。
/** * 設定要載入的圖片的路徑, 可為網路路徑, Asset檔案路徑(file:///android_asset), 本地圖片路徑(file:///或/) * * @param path 要載入的圖片的路徑, 若為null則載入預設圖片 * @param loadMethod 自訂的圖片載入的方法, 可以null, 使用預設的載入方法 * @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)設定使用者自訂的緩衝方法,由此將圖片的載入與緩衝分離開,使使用者可以使用現有的緩衝實現。如要使用者指定了緩衝Key就使用使用者指定的Key,否則就用圖片的路徑作Key。
4.AsyncImageView中的其他重要方法
reload([boolean force]):重新載入
stopLoading():停止載入,如果當前正在載入則沒有效果,如果載入任務在載入線程池隊列中則取消。
setDefaultImage...()類方法:設定預設圖片。
setPause(boolean pause):是否載入圖處
源碼下載:AsyncImage