原文地址:http://developer.android.com/training/displaying-bitmaps/process-bitmap.html html
在非UI線程中處理圖片java
BitmapFactory.decode*方法,已在Load Large BItmaps Efficiently 章節中討論了,若是資源數據從磁盤或是網絡獲取的話不該該在UI線程中執行。圖片數據的獲取所用的時間是未知的,依賴於不少條件。若是其中的一個堵塞了主線程,會致使應用無響應。
android
這節課經過展現使用AsyncTask在後臺線程中處理圖片加載的流程,來解決死鎖的問題。
緩存
使用AsyncTask網絡
AsyncTask類提供了一個簡單的方法讓一些任務在後臺線程中執行,並把結果返回到UI線程中。下面是一個使用AstncTask和decodeSampleBitmapFromResource()加載一個大圖到ImageView的實現的例子:
併發
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); } } } }
使用WeakReference存儲ImageView,可使AsyncTask不會阻止ImageView被垃圾回收。由於沒法保證在任務結束後ImageView還存在,因此在onPostExecute()方法中也必需要校驗。
async
併發處理ide
若是在ListView和GridView這樣的組件中像上一節那樣使用AsyncTask會致使另外一種結果。爲了有效的利用緩存,這些組件在滾動的過程當中會複用子控件。若是每一個子控件只出發一個任務,那個沒法避免當這個任務完成的時候,這個任務關聯的控件正用於展現其餘子視圖。
this
在博客MultithreadingforPerformance中討論併發行爲,並給出了一個解決方案,經過ImageView存儲一個最近的任務AsyncTask,這樣當任務完成時可以被處理。
spa
建立一個專門的Drawable子類存儲回調的任務。這樣當任務返回時就能夠利用這個圖片來展現在ImageView中。
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(); } }
在執行BitmapWorkerTask任務前,須要建立一個AsyncDrawable並綁定到目標ImagView
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); } }
cancelPotentialWork是取消綁定目標ImageView的上一個任務
public static boolean cancelPotentialWork(int data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final int bitmapData = bitmapWorkerTask.data; // If bitmapData is not yet set or it differs from the new data if (bitmapData == 0 || 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; }
getBitmapWorkerTask(),經過ImageView獲取其中的任務
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; }
在任務的onPostExecute()方法須要判斷任務的cancelled狀態
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); } } } }