高效的加载图片整理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/syusikoku/article/details/74359948

高效的加载图片

图片的缩放

public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) {

    // Raw height and width of image

    final int height = options.outHeight;

    有效地加载大尺寸位图(Loading Large Bitmaps Efficiently)

    读取位图的尺寸与类型(Read Bitmap Dimensions and Type)

    加载一个按比例缩小的版本到内存中(Load a Scaled Down

    Version into Memory)

    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;

}

AsyncTask 类提供了一个简单的方法来在后台线程执行一些操作,并且可以把后台的结果呈现到UI线程。下面是一个加载大

图的示例:

class BitmapWorkerTask extends AsyncTask {

    private final WeakReference imageViewReference;

    private int data = 0;

    public BitmapWorkerTask(ImageView imageView) {

        // Use a WeakReference to ensure the ImageView can be garbage collected

        imageViewReference = new WeakReference(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);

            }

        }

    }

}

为ImageView使用WeakReference 确保了 AsyncTask 所引用的资源可以被GC(garbage collected)。因为当任务结束时不能

确保 ImageView 仍然存在,因此你必须在 onPostExecute() 里面去检查引用。这个ImageView 也许已经不存在了,例如,

在任务结束时用户已经不在那个Activity或者是设备已经发生配置改变(旋转屏幕等)。

开始异步加载位图,只需要创建一个新的任务并执行它即可:

public void loadBitmap(int resId, ImageView imageView) {

    BitmapWorkerTask task = new BitmapWorkerTask(imageView);

    task.execute(resId);

}

处理并发问题(Handle Concurrency)

Multithreading for Performance 这篇博文更进一步的讨论了如何处理并发并且提供了一种解决方法,当任务结束时

ImageView 保存一个最近常使用的AsyncTask引用。使用类似的方法, AsyncTask 可以扩展出一个类似的模型。创建一个专

用的 Drawable 子类来保存一个可以回到当前工作任务的引用。在这种情况下,BitmapDrawable 被用来作为占位图片,它可

以在任务结束时显示到ImageView中。

创建一个专用的Drawable的子类来储存返回工作任务的引用。在这种情况下,当任务完成时BitmapDrawable会被使用,

placeholder image才会在ImageView中被显示:

static class AsyncDrawable extends BitmapDrawable {

    private final WeakReference bitmapWorkerTaskReference;

    public AsyncDrawable(Resources res, Bitmap bitmap,BitmapWorkerTask bitmapWorkerTask) {

        super(res, bitmap);

        bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask);

    }

    public BitmapWorkerTask getBitmapWorkerTask() {

        return bitmapWorkerTaskReference.get();

    }

}

在执行BitmapWorkerTask 之前,你需要创建一个 AsyncDrawable 并且绑定它到目标组件 ImageView 中:

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中运行的任务得以取消。如果是这样,它

通过执行 cancel() 方法来取消之前的一个任务. 在小部分情况下, New出来的任务有可能已经存在,这样就不需要执行这个任

务了。下面演示了如何实现一个 cancelPotentialWork 。

public static boolean cancelPotentialWork(int data, ImageView imageView) {

    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

    if (bitmapWorkerTask != null) {

        final int bitmapData = bitmapWorkerTask.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;

}

最后一步是在BitmapWorkerTask 的 onPostExecute() 方法里面做更新操作:

class BitmapWorkerTask extends AsyncTask {

    ...

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

            }

        }

    }

}

这个方法不仅仅适用于 ListView 与 GridView 组件,在那些需要循环利用子视图的组件中同样适用。只需要在设置图片到

ImageView的地方调用 loadBitmap 方法。例如,在GridView 中实现这个方法会是在 getView() 方法里面调用。

BitmapWorkerTask task = new BitmapWorkerTask(mImageView);

task.execute(resId);

完整的示例代码:

private class ImageAdapter extends BaseAdapter {

...

    @Override

    public View getView(int position, View convertView, ViewGroup container) {

        ...

        loadBitmap(imageResIds[position], imageView)

        return imageView;

    }



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

        }

    }



    static class AsyncDrawable extends BitmapDrawable {

        private final WeakReference bitmapWorkerTaskReference;

        public AsyncDrawable(Resources res, Bitmap bitmap,BitmapWorkerTask bitmapWorkerTask) {

            super(res, bitmap);

            bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask);

        }

        public BitmapWorkerTask getBitmapWorkerTask() {

            return bitmapWorkerTaskReference.get();

        }

    }



    public static boolean cancelPotentialWork(int data, ImageView imageView) {

        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

        if (bitmapWorkerTask != null) {

            final int bitmapData = bitmapWorkerTask.data;

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

    }



    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;

    }

}

声明:资源 摘自:Google Android官方培训教程中文版

猜你喜欢

转载自blog.csdn.net/syusikoku/article/details/74359948