在非UI线程处理Bitmap
2016-02-23 14:06
435 查看
译者按: 在Google最新的文档中,提供了一系列含金量相当高的教程。因为种种原因而鲜为人知,真是可惜!Ryan将会细心整理,将之翻译成中文,希望对开发者有所帮助。
本系列是Google关于展示大Bitmap(位图)的官方演示,可以有效的解决内存限制,更加有效的加载并显示图片,同时避免让人头疼的OOM(Out Of Memory)。
-------------------------------------------------------------------------------------
译文:
在高效地加载Bitmap中,我们讨论了BitmapFactory.decode*系列方法,如果源数据来自硬盘或者网络(或者除内存之外的来源),是不应该在主UI线程执行的。这是因为读取这样的数据所需的加载时间是不确定的,它依赖于多种因素(从硬盘或网络的读取速度、图片的大小、CPU的功率等等)。如果这些任务里面任何一个阻塞了UI线程,系统会将你的应用标记为未响应,并且用户可以选择关闭应用(更多信息,请参阅Designing
for Responsiveness)。
这节课将教会你使用AsyncTask在后台线程处理Bitmap并向你展示如何处理并发问题。
使用AsyncTask(异步任务)
AsyncTask类提供了一种简单的方法,可以在后来线程处理一些事情,并将结果返回到UI线程。要使用它,需要创建一个继承于它的子类,并且覆写它提供的方法。这里有一个使用AsyncTask和decodeSampledBitmapFromResource()加载大图片到ImageView中的例子:
ImageView的WeakReference(弱引用)可以确保AsyncTask不会阻止ImageView和它的任何引用被垃圾回收器回收。不能保证在异步任务完成后ImageView依然存在,因此你必须在onPostExecute()方法中检查引用。ImageView可能已经不存在了,比如说,用户在任务完成前退出了当前Activity或者应用配置发生了变化(横屏)。
为了异步加载Bitmap,我们创建一个简单的异步任务并且执行它:
处理并发
常见的View(视图)组件如ListView和GridView在于AsyncTask配合使用的时候引出了另外一个问题,这个我们在上一节中提到过。为了提升内存效率,当用户滚动这些组件的时候进行子视图的回收(主要是回收不可见的视图)。如果每个子视图都触发了一个AsyncTask,无法保证在任务完成的时候,关联视图还没有被回收而被用来显示另一个子视图。此外,也无法保证异步任务结束的循序与它们开始的顺序一致。
Multithreading
for Performance这篇文章深入讨论了如何处理并发问题,并且给出了如何在任务结束的时候检测ImageView存储最近使用的AsyncTask引用的解决方案。使用相似的方法,可以遵循类似的模式来扩展前面的AsyncTask。
创建一个专用的Drawable之类,用来存储worker task的引用。在这种情况下,任务结束的时候BitmapDrawable可以取代图像占位符显示在ImageView中。
在执行BitmapWorkerTask前,你需要创建一个AsyncDrawable并将之绑定到目标ImageView:
在上面的代码示例中引用的cancelPotentialWork方法可以检测一个执行中的任务是否与ImageView有关联。如果有关联,它将通过调用canceel()方法试图取消之前的任务。在少数情况下,新的任务中的数据与现有的任务相匹配,因此不需要做什么。下面是calcelPotentialWork的具体实现:
一个助手方法,getBitmapWorkerTask(),在上面用来检索和指定ImageView相关的任务
最后一步是更新BitmapWorkerTask中的onPostExecute()方法,以便检测与ImageView关联的任务是否被取消或者与当前任务相匹配。
view
source
print?
这里的方法也适合用在ListView、GridView以及其他任何需要回收子视图的组件中。当你只需要为ImageView设置图片,调用loadBitmap就可以了。例如,在GridView中实现的方式是在Adapter的getView()方法中。
本系列是Google关于展示大Bitmap(位图)的官方演示,可以有效的解决内存限制,更加有效的加载并显示图片,同时避免让人头疼的OOM(Out Of Memory)。
-------------------------------------------------------------------------------------
译文:
在高效地加载Bitmap中,我们讨论了BitmapFactory.decode*系列方法,如果源数据来自硬盘或者网络(或者除内存之外的来源),是不应该在主UI线程执行的。这是因为读取这样的数据所需的加载时间是不确定的,它依赖于多种因素(从硬盘或网络的读取速度、图片的大小、CPU的功率等等)。如果这些任务里面任何一个阻塞了UI线程,系统会将你的应用标记为未响应,并且用户可以选择关闭应用(更多信息,请参阅Designing
for Responsiveness)。
这节课将教会你使用AsyncTask在后台线程处理Bitmap并向你展示如何处理并发问题。
使用AsyncTask(异步任务)
AsyncTask类提供了一种简单的方法,可以在后来线程处理一些事情,并将结果返回到UI线程。要使用它,需要创建一个继承于它的子类,并且覆写它提供的方法。这里有一个使用AsyncTask和decodeSampledBitmapFromResource()加载大图片到ImageView中的例子:
01 | class BitmapWorkerTask extends AsyncTask<Integer, Void,Bitmap> { |
02 | private final WeakReference<ImageView> imageViewReference; |
03 | private int data = 0 ; |
04 |
05 | public BitmapWorkerTask(ImageView imageView) { |
06 | // Use a WeakReference to ensure the ImageView can be garbage collected |
07 | imageViewReference = new WeakReference<ImageView>(imageView); |
08 | } |
09 |
10 | // Decode image in background. |
11 | @Override |
12 | protected Bitmap doInBackground(Integer... params) { |
13 | data =params[ 0 ]; |
14 | return decodeSampledBitmapFromResource(getResources(), data, 100 , 100 )); |
15 | } |
16 |
17 | // |
18 | @Override |
19 | protected void onPostExecute(Bitmap bitmap) { |
20 | if (imageViewReference != null && bitmap != null ) { |
21 | final ImageView imageView = imageViewReference.get(); |
22 | if (imageView != null ) { |
23 | imageView.setImageBitmap(bitmap); |
24 | } |
25 | } |
26 | } |
27 | } |
为了异步加载Bitmap,我们创建一个简单的异步任务并且执行它:
1 | public void loadBitmap( int resId, ImageView imageView) { |
2 | BitmapWorkerTask task = new BitmapWorkerTask(imageView); |
3 | task.execute(resId); |
4 | } |
常见的View(视图)组件如ListView和GridView在于AsyncTask配合使用的时候引出了另外一个问题,这个我们在上一节中提到过。为了提升内存效率,当用户滚动这些组件的时候进行子视图的回收(主要是回收不可见的视图)。如果每个子视图都触发了一个AsyncTask,无法保证在任务完成的时候,关联视图还没有被回收而被用来显示另一个子视图。此外,也无法保证异步任务结束的循序与它们开始的顺序一致。
Multithreading
for Performance这篇文章深入讨论了如何处理并发问题,并且给出了如何在任务结束的时候检测ImageView存储最近使用的AsyncTask引用的解决方案。使用相似的方法,可以遵循类似的模式来扩展前面的AsyncTask。
创建一个专用的Drawable之类,用来存储worker task的引用。在这种情况下,任务结束的时候BitmapDrawable可以取代图像占位符显示在ImageView中。
01 | static class AsyncDrawable extends BitmapDrawable { |
02 | private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; |
03 |
04 | public AsyncDrawable(Resources |
05 | BitmapWorkerTask bitmapWorkerTask) { |
06 | super (res, bitmap); |
07 | bitmapWorkerTaskReference = |
08 | new WeakReference<BitmapWorkerTask>(bitmapWorkerTask); |
09 | } |
10 |
11 | public BitmapWorkerTask getBitmapWorkerTask() { |
12 | return bitmapWorkerTaskReference.get(); |
13 | } |
14 | } |
1 | public void loadBitmap( int resId, ImageView imageView) { |
2 | if (cancelPotentialWork(resId, imageView)) { |
3 | final BitmapWorkerTask task = new BitmapWorkerTask(imageView); |
4 | final AsyncDrawable asyncDrawable = |
5 | new AsyncDrawable(getResources(), |
6 | imageView.setImageDrawable(asyncDrawable); |
7 | task.execute(resId); |
8 | } |
9 | } |
01 | public static boolean cancelPotentialWork( int data, ImageView imageView) { |
02 | final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); |
03 |
04 | if (bitmapWorkerTask != null ) { |
05 | final int bitmapData = bitmapWorkerTask.data; |
06 | if (bitmapData != data) { |
07 | // Cancel previous task |
08 | bitmapWorkerTask.cancel( true ); |
09 | } else { |
10 | // The same work is already in progress |
11 | return false ; |
12 | } |
13 | } |
14 | // |
15 | return true ; |
16 | } |
01 | private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { |
02 | if (imageView != null ) { |
03 | final Drawable drawable = imageView.getDrawable(); |
04 | if (drawable instanceof AsyncDrawable) { |
05 | final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; |
06 | return asyncDrawable.getBitmapWorkerTask(); |
07 | } |
08 | } |
09 | return null ; |
10 | } |
view
source
print?
01 | class BitmapWorkerTask extends AsyncTask<Integer, Void,Bitmap> { |
02 | ... |
03 |
04 | @Override |
05 | protected void onPostExecute(Bitmap bitmap) { |
06 | if (isCancelled()) { |
07 | bitmap = null ; |
08 | } |
09 |
10 | if (imageViewReference != null && bitmap != null ) { |
11 | final ImageView imageView = imageViewReference.get(); |
12 | final BitmapWorkerTask bitmapWorkerTask = |
13 | getBitmapWorkerTask(imageView); |
14 | if ( this == bitmapWorkerTask && imageView != null ) { |
15 | imageView.setImageBitmap(bitmap); |
16 | } |
17 | } |
18 | } |
19 | } |
相关文章推荐
- 最简单的java GUI
- iOS开发第三方存储库YTKKeyValueStore
- 反编译jd_gui 去掉行号
- 安卓通知栏自定义布局提示(NotificationCompat.Builder)
- Easyui Datagrid 如何实现后台交互显示用户数据列表
- NSNumber和NSValue
- EasyUI datagrid 在ie8和360兼容模式兼容性问题
- 系统数据文件和信息 《APUE》 Chapter-6
- Qt使用教程之使用Qt Quick UI表单(三)
- 标准IO库 知识点百问《APUE》 chapter-5
- 标准IO库《APUE》 Chapter-5
- POJ 2442 Sequence 堆的思想的应用 STL 堆学习
- UINavigationBar
- Win32项目搭建Duilib界面个人体会总结
- new AlertDialog.Builder 提示错误
- n-Queens(n皇后)问题的两种方法
- 搜索栏有内容搜索商品,清空UITextField返回搜索历史
- POJ—Building a Space Station
- Emulation currently requires hardware acceleration
- UIViewController的切换效果