在非主線程裏處理bitmap

翻譯自點擊打開鏈接

1 圖片壓縮

不能在main ui線程裏load網絡圖片,啓動一個異步task

should not be executed on the main UI thread if the source data is read from disk or a network location (or really any source other than memory)

For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView.

set inSampleSize to truein your BitmapFactory.Options object. 

an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. 


calculate a sample size value that is a power of two based on a target width and height:


public static int calculateInSampleSize(

            BitmapFactory.Options options, int reqWidth, int reqHeight) {

    // Raw height and width of image

    final int height = options.outHeight;

    final int width = options.outWidth;

    int inSampleSize = 1;


    if (height > reqHeight || width > reqWidth) {


        final int halfHeight = height / 2;

        final int halfWidth = width / 2;


        // Calculate the largest inSampleSize value that is a power of 2 and keeps both

        // height and width larger than the requested height and width.

        while ((halfHeight / inSampleSize) > reqHeight

                && (halfWidth / inSampleSize) > reqWidth) {

            inSampleSize *= 2;

        }

    }


    return inSampleSize;

}


To use this method, first decode with inJustDecodeBounds set to true, pass the options through and then decode again using the new inSampleSize value and inJustDecodeBounds set to false:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,

        int reqWidth, int reqHeight) {


    // First decode with inJustDecodeBounds=true to check dimensions

    final BitmapFactory.Options options = new BitmapFactory.Options();

    options.inJustDecodeBounds = true;

    BitmapFactory.decodeResource(res, resId, options);


    // Calculate inSampleSize

    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);


    // Decode bitmap with inSampleSize set

    options.inJustDecodeBounds = false;

    return BitmapFactory.decodeResource(res, resId, options);

}

壓縮圖片啊,這個需要放在AsynTask裏面

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

舉個栗子

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);

            }

        }

    }

}

The WeakReference to the ImageView ensures that the AsyncTask does not prevent the ImageView and anything it references from being garbage collected. There’s no guarantee the ImageView is still around when the task finishes, so you must also check the reference in onPostExecute()

也就是說,使用weakpreference的地方,需要檢查不爲空!!!!The ImageView may no longer exist, if for example, the user navigates away from the activity or if a configuration change happens before the task finishes.

調用的時候,直接new就可以了


To start loading the bitmap asynchronously, simply create a new task and execute it:

public void loadBitmap(int resId, ImageView imageView) {

    BitmapWorkerTask task = new BitmapWorkerTask(imageView);

    task.execute(resId);

}

2對於需要載入多個圖片的控件

 ListView and GridView  In order to be efficient with memory, these components recycle child views as the user scrolls. 

對於這種需要導入多個圖片的控件,需要在drawable裏面維護一個task的引用,並且使用佔位符圖片

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();

    }

}

在開線程前,可以先創建一個AsyncDrawable

Before executing the BitmapWorkerTask, you create an AsyncDrawable and bind it to the target ImageView:

public void loadBitmap(int resId, ImageView imageView) {

    if (cancelPotentialWork(resId, imageView)) {第一個參數是資源id啊?

        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);

        final AsyncDrawable asyncDrawable =

                new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);

        imageView.setImageDrawable(asyncDrawable);

        task.execute(resId);

    }

}

The cancelPotentialWork method referenced in the code sample above checks if another running task is already associated with the ImageView

首先檢測當前imageview上是否有異步task的執行

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說明已經有異步task在work了,就不要再新建了

            return false;

        }

    }

    // No task associated with the ImageView, or an existing task was cancelled

    return true;

}

A helper method, getBitmapWorkerTask(), is used above to retrieve the task associated with a particularImageView:

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;

}

最後一步就是在回調主線程時,顯示bitmap了。

The last step is updating onPostExecute() in BitmapWorkerTask so that it checks if the task is cancelled and if the current task matches the one associated with the 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);

            }

        }

    }

}


發佈了118 篇原創文章 · 獲贊 2 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章