• 在UI线程之外,多线程处理Bitmaps


    多线程处理Bitmaps


        上一篇,我们讨论了:Android有效的处理Bitmap,降低内存 ,可是最好不要运行在主线程(UI线程),假设图片是本地的或者网络的又或者是其它地方的。

    图片载入的时间和很多因素有关(比方从网络或本地读取速度,图片的大小。CPU的能力),假设这些任务堵塞了UI线程,系统有可能会回收并关闭它(see Designing for Responsiveness for more information).

       这篇我们将讨论怎样在UI线程之外后台使用异步任务(AsyncTask)加入Bitmap(本地或者网络图片或者其它资源的图片),而且叫你怎样并发处理他们。

      使用异步任务

        异步任务提供了一种方便的方法来实现UI线程和后台线程的交互。我们能够派生一个类,而且重写他的方法。以下有个载入一张大图到ImageView的样例,使用上篇的降到的技术decodeSampledBitmapFromResource()(请看上篇的内容http://blog.csdn.net/liu8497/article/details/40786721):
    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为了内存可以非常好的回收他们,可是不能保证异步任务结束的时候。该ImageView还存在,所以必须在
    onPostExecute()检查是否为空。该ImageView可能为空,比方在异步任务退出之前。当Activity退出的时候或者其它配置改变的时候
      启动载入一个Bitmap仅仅须要实例化一个任务而且运行它就可以:
    public void loadBitmap(int resId, ImageView imageView) {
        BitmapWorkerTask task = new BitmapWorkerTask(imageView);
        task.execute(resId);
    }

    并行处理

        常规组件。比方ListView和GridView在与异步任务结合的时候引入了其它问题。

    为了更好的使用内存,当用户滚动的时候就開始回收其它子View。假设每一个子View都使用一个异步任务。我们不能保证当异步任务结束的时候。相关联的view没有被回收掉。我们也不能保证异步任务依照我们的顺序运行。

      有一篇博客 Multithreading for Performance,深入的讨论了并行处理的问题,而且提供了一种解决方法。其ImageView指向了近期的一个异步任务,而且能够在任务结束之后检验运行。运用同样的方法,上面提到的异步任务能够遵循使用类似的路线。
      创建一个专用的Drawable来存储指向该任务的资源。所以,我们使用BitmapDrawable,这样在任务结束的时候我们的image就展示在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之前。我们能够给ImageView绑定一个AsyncDrawable。
    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()取消掉关联的任务。

    非常少情况下,新任务的数据与之前的匹配而且交互。以下是cancelPotentialWork的实现:


    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()在BitmapWorkerTask更新为了检验任务是否取消而且检验当前任务是否匹配该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);
                }
            }
        }
    }
    如今。我们就能使用异步任务非常好的在ListView和GridView里面或者其它组件里面更有效的回收资源。

    我们仅仅须要运行loadBitmap 的方法就能放一张图片到ImageVIew里面。

    比方,在GridVIew里面,我们在Adapter的getView方法里面使用这样的方法。


    关于Bitmap的优化。请看上篇:Android有效的处理Bitmap,降低内存

  • 相关阅读:
    面试试题
    使用NSURLSessionDataTask请求数据(get post方式)
    使用SSZipArchive第三方库解压zip包
    实现图文混编界面
    使用多线程创建单例对象
    SQL语句的种类_外键_表连接(内连接和左外连接)
    使用第三方库(FMDB) 本地数据库存储数据 --使用为了保证线程安全做法
    利用第三方库XML解析 (TBXML)转化成模型数据
    蓝牙传送_多点连接 (适用于>iOS7)
    Unity 产生各不相同的随机数
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/6794540.html
Copyright © 2020-2023  润新知