原:http://www.cnblogs.com/angeldevil/archive/2012/09/16/2687174.htmlhtml
相關:https://github.com/nostra13/Android-Universal-Image-Loaderandroid
開發Android程序,通常狀況下都會有兩個操做,圖片的異步加載與緩存,而圖片的異步加載大都是從網絡讀取圖片(還有生成本地圖片縮略圖等操做),爲了減小網絡操做,加快圖片加載速度就須要對圖片進行緩存,因此網上的好多圖片異步加載方法都是與圖片的緩存緊密關聯的。但也有可能用戶已經有了緩存的相關類庫,這樣使用起來就會有點麻煩。git
最近一段處理跟圖片相關的問題,原本是本身寫的圖片加載,不過有些狀態的控制仍是比較煩人的,好比ListView滾動時ImageView的重用,因此本着偷懶與充分利用現有資源的態度去網上搜羅圖片異步加載的代碼,最終在GreenDroid UI庫中找到一個,其中有個AsyncImageView的自定義View用於異步加載圖片,不過也像網上的大多數圖片異步加載方法同樣,是跟圖片的緩存關聯在一塊兒的,不過只是很簡單的內存緩存,無文件緩存。圖片的加載方法也如其餘的同樣是寫死了的,這就限制了其使用範圍,只可經過InputStream來decode圖片,而像生成縮略圖或其餘一些圖片處理的異步處理就沒法用途。修改現有類庫總比本身從頭寫來的簡單,因而稍微修改了下AsyncImageView,使其能夠自定義緩存與圖片加載方法,對於AsyncImageView只有一點點的修改,大都是別人源碼。github
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。緩存
經過AsyncImageView的setPath方法來加載圖片,setPath有3個重載方法:網絡
第一個參數指定要加載的圖片的路徑,第二個參數爲自定義的圖片加載方法,若不指定則用默認的。異步
至於加第三個參數,是作緩存用的,通常要加載的圖片的路徑都是惟一的,因此通常用第一個參數來作爲緩存的Key就好了,但也有特殊狀況,好比讀取局域網中的圖片,通常都是自動獲取IP,因此根據圖片路徑作爲緩存的Key多是不合適的,因此就須要根據須要手動指定用來做爲緩存的Key。fetch
/** * 設置要加載的圖片的路徑, 可爲網絡路徑, 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()用於讀取緩存,代碼以下:this
private Bitmap readCache() { if (mCache != null) return mCache.readCache(TextUtils.isEmpty(mCacheKey) ? mUrl : mCacheKey); return null; }
其中的mCache由用戶能過setCacheCallback(CacheCallback callback)設置用戶自定義的緩存方法,由此將圖片的加載與緩存分離開,使用戶能夠使用現有的緩存實現。如要用戶指定了緩存Key就使用用戶指定的Key,不然就用圖片的路徑做Key。url
源碼下載:AsyncImage