實際的使用環境中,若是圖片來源是SD卡或者網絡,那那麼加載圖片的過程必定不要放在UI線程中,這樣會嚴重的阻塞UI線程,出現ANR,程序就廢了。所以咱們首先要實現異步加載。
第一步:利用AsyncTask實現圖片的異步加載
java
將decodeSampledBitmapFromResource方法放入Task的doInBackground中後臺執行。不熟悉AsyncTask的同窗能夠學習AsyncTask的相關知識,這裏再也不過多介紹。緩存
代碼:網絡
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private int data = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { data = params[0]; return decodeSampledBitmapFromResource(getResources(), data, 100, 100)); } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } }
注意,這裏對ImageView使用 WeakReference弱引用的目的是確保 AsyncTask不會妨礙系統對ImageView必要時候的垃圾回收。不然可能會出現內存泄露,同時,咱們必定要在Task執行完畢後對ImageView的存在性進行判斷,由於不能保證Task執行完畢後,ImageView還會存在。併發
下來咱們按照下面的代碼就可使用這個Task了:異步
public void loadBitmap(int resId, ImageView imageView) { BitmapWorkerTask task = new BitmapWorkerTask(imageView); task.execute(resId); }
第二步:處理併發狀況async
ListView與GridView這種多子視圖的控件會出現兩個問題,
ide
第一,一個ListView會有衆多的ChildView,爲了更高效的利用內存,控件會自動回收掉被用戶滑動過去,不在當前有顯示的ChildView,若是每個ChildView都開啓一個Task去加載圖片,這樣就不能保證開啓Task的ChildView在Task執行完畢後沒有被回收掉(頗有可能用戶滑動到其餘地方去了)。
第二,由於每張圖片的處理時間是不一樣的,所以一樣不能保證加載完成的次序與開始的次序一致。
下來咱們開始着手解決這些問題,咱們要讓ImageView與Task造成一種綁定的關係。
咱們先來建立一個特殊的Drawable,這個Drawable有兩個功能,一個是與Task造成一種綁定的關係,另外也充當了ImageView的臨時佔位圖像,該Drawable的代碼以下:函數
static class AsyncDrawable extends BitmapDrawable { private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask); } public BitmapWorkerTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } }
在該Drawable中經過弱引用能與對應的Task造成一種一一對應的捆綁關係。學習
咱們能夠這樣使用它,在執行Task以前,先建立一個對應的Drawable,並把它當成將要呈現實際圖片的ImageView佔位圖片,同時也與ImageView造成了綁定關係。this
public void loadBitmap(int resId, ImageView imageView) { if (cancelPotentialWork(resId, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); imageView.setImageDrawable(asyncDrawable); task.execute(resId); } }
固然,咱們須要判斷下ImageView以前是否已經綁定了,若是以前綁定過但與本次的圖片不一樣,那咱們就要按最新的須要重新綁定下,若是以前與如今的一致,則保持原狀,再也不重新綁定,代碼中的cancelPotentialWork就是作這個工做的,其代碼以下:
public static boolean cancelPotentialWork(int data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final int bitmapData = bitmapWorkerTask.data; if (bitmapData != data) { // Cancel previous task bitmapWorkerTask.cancel(true); } else { // The same work is already in progress return false; } } // No task associated with the ImageView, or an existing task was cancelled return true; }
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncDrawable) { final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null; }
最後,咱們在Task的onPostExecute函數中,把加載的圖片更新到視圖中去,在更新前咱們須要檢查下Task是否被取消,而且當前的Task是不是那個與ImageView關聯的Task,一致則咱們把圖片更新到ImageView上去,代碼以下:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { ... @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } }
最後,實際的使用也至關簡單,只須要在你的ListView適配器的getView函數中調用上面的loadBitmap函數就OK了~
下一節咱們來講說緩存,加入緩存讓這個機制更增強大。。
感謝收看! 多多好評,在此謝過!