Processing Bitmaps Off the UI Thread [在UI Thread之外处理Bitmap]
  • 在上一课中有介绍一系列的BitmapFactory.decode* 方法,当数据源是网络或者是磁盘时(或者是任何实际源不在内存的),这些方法都不应该在main UI 线程中执行。那些情况下加载数据是不可以预知的,它依赖于许多因素(从网络或者硬盘读取数据的速度, 图片的大小, CPU的速度, etc.)。如果其中任何一个任务卡住了UI thread, 系统会出现ANR的错误。
  • 这一节课会介绍如何使用 AsyncTask 在后台线程中处理bitmap并且演示了如何处理并发(concurrency)的问题。

Use an AsyncTask [使用AsyncTask]

  • AsyncTask类提供了一个简单的方法来在后台线程执行一些操作,并且可以把后台的结果呈现到UI线程。下面是一个加载大图的示例:
  • 关于如何使用AsyncTask,我的博文里面有一篇文章也有简单介绍,【Android】使用AsyncTask来处理一些简单的需要后台处理的动作
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 [处理并发问题]

  • 通常类似ListViewGridView等视图组件在使用上面演示的AsyncTask方法时会同时带来另外一个问题。为了更有效的处理内存,那些视图的子组件会在用户滑动屏幕时被循环使用(关于这个原理请参考【Android】ListView中getView的原理与解决多轮重复调用的方法). 如果每一个子视图都触发一个AsyncTask ,那么就无法确保当前视图在结束时,分配的视图已经进入循环队列中给另外一个子视图进行重用。而且, 无法确保所有的异步任务能够按顺序执行完毕。
  • Multithreading for Performance这篇博文更进一步的讨论了如何处理并发并且提供了一种解决方法,当任务结束时ImageView保存一个最近常使用的AsyncTask引用。(暂时打不开那篇博文,下次试试看,仔细看下) 。使用类似的方法, AsyncTask可以扩展出一个类似的模型.
  • 创建一个专用的Drawable子类来保存一个可以回到当前工作任务的引用。 在这种情况下,BitmapDrawable被用来作为占位图片,它可以在任务结束时显示到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 != 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;}
  • 最后一步是在BitmapWorkerTaskonPostExecute()方法里面做更新操作:
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);            }        }    }}
  • 这个方法不仅仅适用于ListViewGridView组件,在那些需要循环利用子视图的组件中同样适用。只需要在设置图片到ImageView的地方调用loadBitmap方法。例如,在GridView中实现这个方法会是在getView()方法里面调用。

学习自:http://developer.android.com/training/displaying-bitmaps/process-bitmap.html,请多指点,谢谢!

转载请注明出处:http://blog.csdn.net/kesenhoo,谢谢配合!










更多相关文章

  1. Android退出方式
  2. Android使用SQLITE3 WAL模式
  3. Android(安卓)面试题目总结【持续更新...】
  4. Android模糊图像
  5. Android获取三轴加速度和view的重绘
  6. android应酬资料
  7. 自定义UI框架
  8. Android自动更新:这里的更新静悄悄~
  9. Android原生与H5交互的实现

随机推荐

  1. Android Layout属性笔记
  2. 《Android 从初学者入门到成为高手 视频
  3. Android使用fitsSystemWindows属性实现–
  4. Android圆形水波纹按钮的实现(Ripple)
  5. Android技能树 — 排序算法基础小结
  6. android 写 xml时,加layout与不加的区别(如
  7. android:RecyclerView局部刷新那点事~
  8. Android开发工具——ADB(Android(安卓)De
  9. Android热更新实现原理
  10. Android入门学习:Android 系统框架及应用