Android應用開發圖片非同步載入

來源:互聯網
上載者:User

眾所周知Android應用開發中不能在UI線程中做耗時的操作,否則就會彈出煩人的ANR視窗。

應用開發中如果需要載入來自網路、磁碟或其他非記憶體中圖片資源時,因載入時間會受到其他因素(如磁碟、網路、圖片大小、CPU等等)的影響,很容易產生耗時操作。所以在進行類似操作時要避免在UI線程中進行。今天就和大家分享一下如何通過AsyncTask非同步載入圖片和怎麼處理多線程並發問題。

如何使用 AsyncTask載入圖片?

通過AysncTask可以很容易的在啟動後台線程載入資源,然後將結果返回到UI線程中。使用它時,需要建立它的子類並實現相應的方法,如下是一個通過AysncTask和decodeSampledBitmapFromResource()方法載入一張大圖片到ImageView中的例子:

 1 class BitmapWorkerTask extends AsyncTask { 2     private final WeakReference imageViewReference; 3     private int data = 0; 4                                                                         5     public BitmapWorkerTask(ImageView imageView) { 6         // Use a WeakReference to ensure the ImageView can be garbage collected 7         imageViewReference = new WeakReference(imageView); 8     } 9                                                                                                                                                                                       10     // Decode image in background.11     @Override12     protected Bitmap doInBackground(Integer... params) {13         data = params[0];14         return decodeSampledBitmapFromResource(getResources(), data, 100, 100));15     }16                                                                                                                                                                                     17     // Once complete, see if ImageView is still around and set bitmap.18     @Override19     protected void onPostExecute(Bitmap bitmap) {20         if (imageViewReference != null && bitmap != null) {21             final ImageView imageView = imageViewReference.get();22             if (imageView != null) {23                 imageView.setImageBitmap(bitmap);24             }25         }26     }27 }

使用WeakReference 儲存ImageView的原因,是為了在記憶體資源緊張時確保AsyncTask 不會阻止對其進行資源回收,因此當task結束時不能保證Imageview還存在,所以你應該在onPostExecute中對它進行驗證(本例中在Task結束前如果使用者關閉Activity,或系統設定改變時,ImageView可能會被回收)。

通過以下方式我們就可以非同步載入圖片:

1 public void loadBitmap(int resId, ImageView imageView) {2     BitmapWorkerTask task = new BitmapWorkerTask(imageView);3     task.execute(resId);4 }

如何處理並行作業?

常用的View組件中 像ListView、GridView等 為了高效實用記憶體,使用者在進行View滾動操作時系統會對不再使用子View進行資源回收,,採用上面的方式進行圖片載入時會引入另外一個問題。如果在每個子View中開啟AsyncTask,不能保證在任務完成時,相關的View是否已經被回收。此外,也不能保證他們載入完成的順序

我們可以通過將AsyncTask的引用儲存ImageView關聯Drawable中,任務完成時檢查引用是否存在.

建立一個專用的Drawable子類,儲存工作任務線程的引用。這樣在任務完成時即可將圖片設定在ImageView中

 1 static class AsyncDrawable extends BitmapDrawable { 2     private final WeakReference bitmapWorkerTaskReference; 3     public AsyncDrawable(Resources res, Bitmap bitmap, 4             BitmapWorkerTask bitmapWorkerTask) { 5         super(res, bitmap); 6         bitmapWorkerTaskReference = 7             new WeakReference(bitmapWorkerTask); 8     } 9                                                                  10     public BitmapWorkerTask getBitmapWorkerTask() {11         return bitmapWorkerTaskReference.get();12     }13 }

在執行BitmapTask前,你可以建立AsyncDrawable並將其綁定到ImageView中

1 public void loadBitmap(int resId, ImageView imageView) {2     if (cancelPotentialWork(resId, imageView)) {3         final BitmapWorkerTask task = new BitmapWorkerTask(imageView);4         final AsyncDrawable asyncDrawable =5                 new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);6         imageView.setImageDrawable(asyncDrawable);7         task.execute(resId);8     }9 }

上面代碼中通過cancelPotentialWork判斷是否已經存在正在啟動並執行任務綁定在ImageView中,若有,通過執行任務cancel方法取消它,當然這種情況不常發生,

下面是cancelPotentialWork的實現:

 1 public static boolean cancelPotentialWork(int data, ImageView imageView) { 2     final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); 3                4     if (bitmapWorkerTask != null) { 5         final int bitmapData = bitmapWorkerTask.data; 6         if (bitmapData != data) { 7             // Cancel previous task 8             bitmapWorkerTask.cancel(true); 9         } else {10             // The same work is already in progress11             return false;12         }13     }14     // No task associated with the ImageView, or an existing task was cancelled15     return true;16 }

下面是一個輔助方法,通過ImageView尋找與其關聯的非同步任務;

 1 private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { 2    if (imageView != null) { 3        final Drawable drawable = imageView.getDrawable(); 4        if (drawable instanceof AsyncDrawable) { 5            final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; 6            return asyncDrawable.getBitmapWorkerTask(); 7        } 8     } 9     return null;10 }

下一步需要在BitmapWorkerTask中的onPostExecute中執行更新操作,

首先檢查任務是否取消,如後更行與其關聯的ImageView:

 1 class BitmapWorkerTask extends AsyncTask { 2     ...                                                                                                                                     3     @Override 4     protected void onPostExecute(Bitmap bitmap) { 5         if (isCancelled()) { 6             bitmap = null; 7         }                                                                                                                              8         if (imageViewReference != null && bitmap != null) { 9             final ImageView imageView = imageViewReference.get();10             final BitmapWorkerTask bitmapWorkerTask =11                     getBitmapWorkerTask(imageView);12             if (this == bitmapWorkerTask && imageView != null) {13                 imageView.setImageBitmap(bitmap);14             }15         }16     }17 }

通過以上方法,你就可以在ListView、GridView或者其他具有子view回收處理的組件中使用,通過調用

loadBitmap你可以很簡單的添加圖片到ImageView中,如:在GirdView的 Adapter中的getView方法中調用。

相關文章

聯繫我們

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

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

Tags Index: