原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/process-bitmap.html

我们在上节课Load Large Bitmaps Efficiently中讨论了BitmapFactory.decode*方法,说到了不应该在UI线程中执行读取数据的过程,尤其是从磁盘或者网络上读取数据(或者其它读取速度次于内存的地方)。读取数据的时间是不可预料的,这取决于各种各样的因素(从磁盘或者网络读取的速度、图片的大小、CPU的功率,etc.)。如果这其中的一个因素阻塞了UI线程,那么系统会标志程序为无响应标志,并会给用户提供一个关闭的选项(请查看Designing for Responsiveness获取更多信息)。

这节课讨论了通过使用AsyncTask在非UI线程中处理位图以及展示如何处理并发问题。

使用AsyncTask

类AsyncTask提供了一种简要的方式来处理后台进程的工作,并会将处理后的结果推送到UI线程中。如果要使用这个类,需要创建该类的子类,然后重写所提供的方法。这里有个例子,展示了如何使用AsyncTask及decodeSampledBitmapFromResource()来加载一张大图到ImageView上:

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 collectedimageViewReference = new WeakReference<ImageView>(imageView);}// Decode image in background.@Overrideprotected Bitmap doInBackground(Integer... params) {data = params[0];return decodeSampledBitmapFromResource(getResources(), data, 100, 100));}// Once complete, see if ImageView is still around and set bitmap.@Overrideprotected void onPostExecute(Bitmap bitmap) {if (imageViewReference != null && bitmap != null) {final ImageView imageView = imageViewReference.get();if (imageView != null) {imageView.setImageBitmap(bitmap);}}}
}

ImageView的WeakReference可以确保AsyncTask不会阻止ImageView及它所引用的事务被垃圾回收器回收。这不能保证在任务执行完毕的时候ImageView还依然存在,所以你还必须在onPostExecute()方法中检查一下它的引用。ImageView可能已经不存在了,比如说吧,当用户离开了activity或者在任务结束的时候一些配置发生了变化。

为了启动异步任务来加载图片,需要简单的创建一个新任务并执行它:

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

处理并发

一些普通的View控件比如ListView和GridView会涉及到另一个问题,就是当与AsyncTask结合使用的时候会出现并发问题。为了能有效的使用内存,这些控件会随着用户的滑动来回收子View。如果每一个子View都会触发一个AsyncTask,那么就不能保障在任务完成的时候,与之相关联的View没有被回收利用。此外,对于顺序启动的任务也不能保障可以按顺序完成。

博客Multithreading for Performance进一步的讨论了如何处理并发,它提供了一个解决方案:在ImageView中存储了最近的AsyncTask的引用,这个引用可以在任务完成的时候对最近的AsyncTask进行检查。通过类似的办法,那么上面章节的AsyncTask可以被扩展成类似的模式。

创建一个专用的Drawable子类来存储工作任务的引用。在这种情况下,BitmapDrawable就会被用到,所以在任务完成之前可以有一个占位图显示在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任务之前,你可以创建一个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()尝试取消原来的任务。在少数情况下,新建的任务数据可能会与已经存在的任务相匹配,所以就不要有进一步的动作。下面是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 dataif (bitmapData == 0 || bitmapData != data) {// Cancel previous taskbitmapWorkerTask.cancel(true);} else {// The same work is already in progressreturn false;}}// No task associated with the ImageView, or an existing task was cancelledreturn 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(),所以它会检查任务是否已经被取消和检查当前的任务是否与与之相关联的ImageView相匹配:

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {...@Overrideprotected 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设置图片了。比如,在一个GridView的实现中,这个方法就可以在相应适配器的getView()方法中使用。

Android官方开发文档Training系列课程中文版:高效显示位图之在非UI线程中处理图片相关推荐

  1. Android官方开发文档Training系列课程中文版:目录

    原文地址 : http://android.xsoftlab.net/training/index.html 引言 在翻译了一篇安卓的官方文档之后,我觉得应该做一件事情,就是把安卓的整篇训练课程全部翻 ...

  2. Android官方开发文档Training系列课程中文版:创建自定义View之View的创建

    原文地址:http://android.xsoftlab.net/training/custom-views/index.html 引言 Android框架含有大量的View类,这些类用来显示各式各样 ...

  3. Android官方开发文档Training系列课程中文版:OpenGL绘图之图形绘制

    原文地址:http://android.xsoftlab.net/training/graphics/opengl/draw.html 如果你还不清楚如何定义图形及坐标系统,请移步:Android官方 ...

  4. Android官方开发文档Training系列课程中文版:使用Fragment构建动态UI之Fragment创建

    原文地址:http://android.xsoftlab.net/training/basics/fragments/index.html 导言 为了在Android中创建动态的多面板用户界面,你需要 ...

  5. Android官方开发文档Training系列课程中文版:高效显示位图之加载大位图

    原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/index.html 引言 学习如何使用一种常规的手段来处理及加载Bitmap ...

  6. Android官方开发文档Training系列课程中文版:高效显示位图之位图缓存

    原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/cache-bitmap.html 往UI界面中加载单张图片的过程是很简单的, ...

  7. Android官方开发文档Training系列课程中文版:打印内容之HTML文档打印

    原文地址:http://android.xsoftlab.net/training/printing/html-docs.html 在Android中打印内容要比打印照片要复杂一些,它要求将文本与图像 ...

  8. Android官方开发文档Training系列课程中文版:布局性能优化之布局层级优化

    原文地址:http://android.xsoftlab.net/training/improving-layouts/index.html 引言 布局是直接影响用户体验的关键部分.如果实现的不好,那 ...

  9. Android官方开发文档Training系列课程中文版:线程执行操作之创建多线程管理器

    原文地址:http://android.xsoftlab.net/training/multiple-threads/create-threadpool.html 上节课我们学习了如何定义一个任务.如 ...

  10. Android官方开发文档Training系列课程中文版:电池续航时间优化之监测电池电量及充电状态

    原文地址:http://android.xsoftlab.net/training/monitoring-device-state/index.html 引言 作为一款优秀的APP应用,应该总是想方设 ...

最新文章

  1. kafka安装_安装Kafka
  2. 30分钟全面解析-SQL事务+隔离级别+阻塞+死锁
  3. wgn和awgn函数
  4. *【HDU - 4006】 The kth great number(优先队列 or 线段树)
  5. 监督学习 | 非线性回归 之多项式回归原理及Sklearn实现
  6. RewriteCond 和RewriteRule
  7. Web前端小例子——简单导航栏
  8. vue-router: $router.push遇到的问题
  9. 无法安装某些更新或程序
  10. 该内存不能为read解决办法
  11. Win7和win10下python3和python2同时安装并解决pip共存问题
  12. SpringBoot SSM 心理咨询论坛社区
  13. win8文件共享服务器搭建,Win8系统开启公用文件夹共享的方法【图文】
  14. Cisco Packet Tracer思科模拟器中路由器PPP封装与验证
  15. 【张朝阳的物理课笔记】 1. 力,牛顿定律,飞船角速度
  16. 连接到此计算机的本地打印机无法选择,Win7系统连接打印机出现本地打印后台处理程序服务没有运行怎么办...
  17. linux执行历史命令用哪个键,Linux中如何使用history命令即历史命令
  18. 全国高级项目经理人数知多少?(数据统计截止2013年6月22日)
  19. 打印1000年到2000年之间的闰年
  20. 三星c7语言设为英文,三星C7手机如何更改显示语言?简单几步就可轻松搞定!

热门文章

  1. 给年薪不到48w的程序员提个醒!!
  2. jquery ajax 异步分页,jquery 分页 Ajax异步
  3. 饥荒联机版服务器显示错误,小白求问 搭服务器出现这种情况是怎么回事
  4. 大数据——sqoop操作mysql和hive导出导入数据
  5. python中内置的集成开发工具_python应用(3):启用集成开发工具pycharm
  6. python游戏开发工程师_Python开发工程师-入门与实战视频课程
  7. linux数字雨代码解释,linux提权 漏洞合集 linux-kernel-exploits
  8. bp神经网络预测_股指期货价格变动趋势往往反映的是股票价格的走势,因此BP神经网络对股指期货价格的准确预测就是对股票价格的准确预测。...
  9. LeetCode 1570. 两个稀疏向量的点积(哈希)
  10. LeetCode MySQL 570. 至少有5名直接下属的经理