zoukankan      html  css  js  c++  java
  • Android Load Picture Asynchronously

    众所周知Android应用开发中不能在UI线程中做耗时的操作,否则就会弹出烦人的ANR窗口。

    应用开发中如果需要加载来自网络、磁盘或其他非内存中图片资源时,因加载时间会受到其他因素(如磁盘、网络、图片大小、CPU等等)的影响,很容易产生耗时操作。所以在进行类似操作时要避免在UI线程中进行。今天就和大家分享一下如何通过AsyncTask异步加载图片和怎么处理多线程并发问题。

    如何使用 AsyncTask加载图片?

    通过AysncTask可以很容易的在启动后台线程加载资源,然后将结果返回到UI线程中。使用它时,需要创建它的子类并实现相应的方法,如下是一个通过AysncTask和decodeSampledBitmapFromResource()方法加载一张大图片到ImageView中的例子:

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

    使用WeakReference 保存ImageView的原因,是为了在内存资源紧张时确保AsyncTask 不会阻止对其进行资源回收,因此当task结束时不能保证Imageview还存在,所以你应该在onPostExecute中对它进行验证(本例中在Task结束前如果用户关闭Activity,或系统设置改变时,ImageView可能会被回收)。

    通过以下方式我们就可以异步加载图片:

    1
    2
    3
    4
    public void loadBitmap(int resId, ImageView imageView) {
        BitmapWorkerTask task = new BitmapWorkerTask(imageView);
        task.execute(resId);
    }

    如何处理并发操作?

    常用的View组件中 像ListView、GridView等 为了高效实用内存,用户在进行View滚动操作时系统会对不再使用子View进行资源回收,,采用上面的方式进行图片加载时会引入另外一个问题。如果在每个子View中开启AsyncTask,不能保证在任务完成时,相关的View是否已经被回收。此外,也不能保证他们加载完成的顺序

    我们可以通过将AsyncTask的引用保存ImageView关联Drawable中,任务完成时检查引用是否存在.

    创建一个专用的Drawable子类,存储工作任务线程的引用。这样在任务完成时即可将图片设置在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();
        }
    }

    在执行BitmapTask前,你可以创建AsyncDrawable并将其绑定到ImageView中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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 != 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;
    }

    下面是一个辅助方法,通过ImageView查找与其关联的异步任务;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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中执行更新操作,

    首先检查任务是否取消,如后更行与其关联的ImageView:

    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或者其他具有子view回收处理的组件中使用,通过调用

    loadBitmap你可以很简单的添加图片到ImageView中,如:在GirdView的 Adapter中的getView方法中调用。

  • 相关阅读:
    【欧拉质数筛选法 模版】
    【归并排序 逆序对 模版】
    【 lca倍增模板】
    【LSGDOJ 1333】任务安排 dp
    【NOIP2013】火柴排队
    【USACO Feb 2014】Cow Decathlon
    【USACO08NOV】奶牛混合起来Mixed Up Cows
    【LSGDOJ 1351】关灯
    【USACO】干草金字塔
    【USACO】电子游戏 有条件的背包
  • 原文地址:https://www.cnblogs.com/qiengo/p/2519168.html
Copyright © 2011-2022 走看看