在非主线程里处理bitmap

来源:互联网 发布:pdf sql pan 编辑:程序博客网 时间:2024/05/22 03:52

翻译自点击打开链接

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

            }

        }

    }

}


0 0
原创粉丝点击