高效加載圖片(三)

原文地址: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);
            }
        }
    }
}
相關文章
相關標籤/搜索