在非 UI 线程来处理图片

作者: rain 分类: 移动 发布时间: 2012-04-15 17:39 6 条评论

高效的加载大尺寸图片 中介绍的 用来解析图片的 BitmapFactory.decode* 函数,需要在非UI线程中调用。
如果是读取网络图片或者磁盘图片,在UI线程中可能会导致程序ANR;如果是解析已经在内存中的图片,则可以在UI线程中调用这些函数。

这节内容介绍如何使用AsyncTask 来处理图片,以及如何处理并发访问。

使用 AsyncTask

AsyncTask 类是一个在后台线程中处理任务,并把结果反馈给UI线程的工具类。使用该类,只需要继承她并且重写对应的函数即可。
下面的代码演示了如何用这个类来载入一个大尺寸图片并显示在
ImageView 中:

引用到
ImageView
WeakReference
确保
AsyncTask
不会直接引用 ImageView 从而导致其无法被垃圾回收。
当解析完图片后,无法确保 ImageView 是否还存在,所以需要在 onPostExecute()
函数中检查下,如果用户在图片加载期间离开了当前界面,则当图片加载完后用来显示图片的 ImageView可能已经不存在了。

只要创建该Task并执行即可开始异步加载图片了:

处理并发访问

ListViewGridView
这种控件使用上面介绍的方式来载入图片可能会引入新的问题。为了提高内存的使用率,当用户做滚动操作的时候这些控件会重复利用子控件,如果每个子控件都触发一个AsyncTask,当任务完成的时候 无法保证该子控件是否已经被重用了。甚至,这些任务开始的顺序和完成的顺序也是不一样的。

这篇博文Multithreading for Performance 进一步
讨论了如何处理并发操作,并且提供了一个解决方案:
ImageView 中保存了最近触发的AsyncTask对象,当任务完成的时候可以用来检测该引用的对象。
使用相似的函数,在前面介绍的AsyncTask对象可以使用相似的模式来扩展。

创建一个特殊的 Drawable 子类来保存载入图片的Task引用。
这里使用 BitmapDrawable 类,这样当任务完成的时候可以直接在
ImageView 中显示:

在执行 BitmapWorkerTask之前,您需要创建一个
AsyncDrawable 并且绑定到需要显示该图片的那个 ImageView中:

上面代码中的 cancelPotentialWork 函数用来检测是否已经有一个Task绑定到 ImageView了。
如果已经有个Task了就尝试取消这个Task(调用 cancel()函数)。
在一些情况下,如果新的任务和已经存在任务的数据一样,则不需要额外的处理。下面是 cancelPotentialWork 函数的一种实现方式:

上面用到的助手函数 getBitmapWorkerTask(),用来获取和 ImageView关联的任务:

最后,需要更新 BitmapWorkerTask类的 onPostExecute() 函数。
用来检测任务是否取消了,以及当前的任务和 ImageView引用的任务是否为同一个任务:

这样就可以在 ListViewGridView
控件中使用该图片加载任务了。只需要在您设置
ImageView图片的地方调用
loadBitmap 函数即可。
例如:在 GridView 中可能需要在Adapter中的 getView() 函数中调用
loadBitmap 函数。

本文出自 云在千峰,转载时请注明出处及相应链接。

本文永久链接: http://blog.chengyunfeng.com/?p=394

Ɣ回顶部