【Android Developers Training】 57. 在UI线程之外处理图像
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。
原文链接:http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
在上节课(博客链接:http://www.cnblogs.com/jdneo/p/3514060.html)中所讨论的BitmapFactory.decode*方法,在数据源是从闪存盘或者网络(任何非手机存储的数据来源)读取的,那么就不能再主UI线程中执行。因为加载这个数据所花费的时间是不可估计的,并且它依赖于虚度因素(从网络读取的速度,图片尺寸,CPU的处理能力,等等)。如果其中一个任务阻塞的UI线程,那么系统将会把你的应用标记为未响应,之后用户就可以选择强制关闭它(可以阅读:Designing for Responsiveness)。
这节课会教你如何在一个后台线程,使用AsyncTask处理图像,并告诉你如何处理并发问题。
一). 使用一个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 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);
}
}
}
}
对于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是否会因为另一个子view也引用同样的对象而不被回收,这一方面是没有保证的。另外,异步任务结束的顺序是否和开始的顺序保持一致,这一点也未必。
在这篇博客:Multithreading for Performance中进一步讨论了处理并发的问题,并提供了一种解决方案,这个方案能让ImageView存储一个最新的AsyncTask引用,同时在任务执行完毕后可以对其进行检查。还是像之前章节那样类似的方法,对AsyncTask进行一些扩展。
创建一个专用的Drawable子类,用来存储“WorkerTask”的引用。在这个例子中,一个BitmapDrawable被使用到,这样的话一个“占位符式的”图片就能在任务完成之前被显示:
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 != 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;
}
最后一步是修改BitmapWorkerTask中的onPostExecute()方法,这样它就能检查任务是否取消了以及当前的任务是否和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和其它需要回收子view的组件来说,就变的更加合适了。只需要调用loadBitmap()就可以对你的ImageView设置图片。例如,在一个GridView的实现中,是在其对应适配器的getView()方法中执行。
【Android Developers Training】 57. 在UI线程之外处理图像的更多相关文章
- 【Android Developers Training】 55. 序言:高效显示位图
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 60. 在你的UI中显示位图
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- android 在UI线程之外处理Bitmap - 开发文档翻译
由于本人英文能力实在有限,不足之初敬请谅解 本博客只要没有注明“转”,那么均为原创,转贴请注明本博客链接链接 Processing Bitmaps Off the UI Thread 在UI线程之外处 ...
- 【Android Developers Training】 3. 构建一个简单UI
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 19. 序言:通过Fragments构建动态UI
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 21. 创建一个可变动的UI
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 108. 使用模拟定位进行测试
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 98. 获取联系人列表
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 【Android Developers Training】 79. 连接到网络
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
随机推荐
- 利用有限自动机(finite automata)进行模式匹配
一.有限自动机定义及基本术语: 一个有限自动机 M 是一个5元组(Q, ,A, Σ, δ),其中: Q 是所有状态的有限集合; ∈ Q (属于)是初始状态; A ⊆ Q (子集)是接受状态的集合; ...
- struts-config.xml的配置
1.<struts-config> 元素 <struts-cofnig> 元素是 Struts 配置文件的根元素.<struts-config> 元素有 8 个子 ...
- event对应的各种坐标
IE8不支持的PageXY 相对于整个页面鼠标的位置 包括溢出的部分 event.pageX; event.pageY; 所有浏览器支持的: 相对于当前浏览器窗口可视区域的坐标event.clie ...
- 12、借助Jacob实现Java打印报表(Excel、Word)
12.使用Jacob来处理文档 Word或Excel程序是以一种COM组件形式存在的.如果能够在Java中调用相应组件,便能使用它的方法来获取文档中的文本信息.Jacob是一个JAVA到微软的COM接 ...
- iOS 比较版本号大小的方法
比较iOS版本号大小 eg:3.2.0和3.1.0 或者 3.2.0和3.2比较 返回bool值变量YES or NO -(BOOL)compareVesionWithServerVersion:(N ...
- object-fit 解决图片指定大小被压缩问题
object-fit 解决图片指定大小被压缩问题 第一次遇到这个属性,是在给video 写 poster的时候,选取的作为poster的img的尺寸有点小,导致video播放器两边有留白.在控制台查看 ...
- [大数据]-Logstash-5.3.1的安装导入数据到Elasticsearch5.3.1并配置同义词过滤
阅读此文请先阅读上文:[大数据]-Elasticsearch5.3.1 IK分词,同义词/联想搜索设置,前面介绍了ES,Kibana5.3.1的安装配置,以及IK分词的安装和同义词设置,这里主要记录L ...
- 基于react的简单TODOList
前段时间看了下react,写个栗子记录 页面效果如下 效果:展示代办事件,正文加了删除线的是已经完成的,未加横杠的是未完成的, 交互:1.在input里面输入新添加的内容,点击Add按钮添加代办事件 ...
- [转]ef使用dbfirst方式连接mysql
为了学习ORM,选择了EntityFramework,经历了三天两夜的煎熬,N多次错误,在群里高手的帮助下,终于成功,现在将我的心路历程记录下来,一是让自己有个记录,另外就是让其它人少走些弯路. 我的 ...
- 【WPF】如何获取命令行参数
应用程序以 Main 方法为入口点,如果为 Main 方法指定一个 string[] 类型的参数,在程序运行时,这个参数会获取到传递给应用程序的命令行参数. 而 WPF 应用程序比较特殊,因为在VS中 ...