原文自:http://android.eoe.cn/topic/ui
BitmapFactory的decode()方法,在Load Large Bitmaps Efficiently要点中进行讨论,不应该执行在主UI线程如果要读取源数据从磁盘或网络位置(或相对内存来说任何别的真实来源).该数据需要加载的时间是不可预知的,并取决于多种因素(从磁盘或网络的读取速度,图像大小,CPU的功率,等).如果这些任务阻塞UI线程,系统标志您的应用程序无响应,用户可以选择关闭它响应(有关更多信息,请参阅Designing for Responsiveness).
这节课将引导您通过在后台线程中使用AsyncTask处理位图,并告诉您如何处理并发问题.
使用一个异步任务
AsyncTask类提供了一种简单的方式来在一个后台线程中执行许多任务,并且把结果反馈给UI线程.使用的方法是,创建一个继承与它的子类并且实现提供的方法.这里是一个使用AsyncTask和decodeSampledBitmapFromResource()加载一个大图片到ImageView中的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
|
把ImageView设置成WeakReference,是因为这能够确保它所指向的ImageView和任何东西在垃圾回收时不被AsyncTask所阻止掉. 并不能保证ImageView在任务要结束时仍然存在,所以你必须在onPostExecute()方法中检查它的引用. ImageView可能不再存在了,例如,如果在任务要结束之前用户已经离开了当前Activity或者屏幕发生了旋转.
为了异步地加载位图,简单地创建一个新的任务并且执行它:
1 2 3 4 |
public void loadBitmap(int resId, ImageView imageView) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(resId);
}
|
处理并发
常见的视图组件例如ListView和GridView如在上一节中当和AsyncTask结合使用时引出了另外一个问题.为了优化内存,当用户滚动时这些组件回收了子视图.如果每个子视图触发一个AsyncTask,当它完成时没法保证,相关的视图还没有被回收时已经用在了别的子视图当中.此外,还有异步任务开始的顺序是不能保证他们完成的顺序.
这篇文章透过Multithreading for Performance功能讨论处理并发,并且提供了一个当任务完成后ImageView将一个引用存储到后面能被检查的AsyncTask的解决方案.使用类似的方法,从上一节的AsyncTask可以扩展到遵循类似的模式.
创建一个专用的Drawable的子类来存储一个引用备份到工作任务中.在这种情况下,一个BitmapDrawable被使用以便任务完成后一个占位符图像可以显示在ImageView中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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:
1 2 3 4 5 6 7 8 9 |
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);
}
}
|
如果别的正在运行的任务已经和这个ImageView关联,cancelPotentialWork引用在上面的代码示例检查中.如果这样,它试图通过调用cancel())取消先前的任务.在少数情况下,新的任务数据匹配现有的任务,而且并不需要做什么.下面是实现 cancelPotentialWork:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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相关的任务:
1 2 3 4 5 6 7 8 9 10 |
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关联时进行检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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);
}
}
}
}
|
现在这个实现适合使用ListView和GridView控件组件以及回收其子视图的任何其他组件.在你正常地给你的ImageView控件设置图片时简单地调用loadBitmap就行了.例如,在一个GridView中实现的方式是在支持的适配中的android.view.View, android.view.ViewGroup) getView()方法中.
相关推荐
示例演示如何从主 UI 线程有效地加载大位图、缓存位图(在内存和磁盘中)、管理位图内存以及在 UI 元素(如 ViewPager 和 ListView/GridView)中显示位图。 介绍 这是 Android Training 类的示例应用程序 。 它演示...
Android 媒体数据库示例(原始 DisplayingBitmaps 示例 ( ))当前版本 1.0 Beta 示例演示如何从主 UI 线程有效地从 Android sd 卡(图库)加载大型位图、缓存位图(在内存和磁盘中)、管理位图内存并在 UI 元素 - ...
第六章 创建多线程SDI应用程序 19 第七章 创建MDI应用程序 23 第八章 分隔窗口 26 第九章 GDI的封装 31 第十章 CString及其它 34 第十一章 动态数据交换(DDX)的WTL支持 35 第十二章 WTL向导 37 第十三章 WTL例程 ...
后台任务 这个 Android 库引入了一些使用线程的React性...您想从源中获取一些“结果对象”——来自 url 的位图对象 然后你创建 SourceHandler<String> 对象,它可以从 url 制作图像 您创建 ResultHandler 对象,您可以
帮手这是Android的帮助... 对于(在接下来的UI线程循环即引起重新布局,而不是现在的)使用视图助手ActivityResultHelper用于使用结果代码简化开始活动(MainActivity中不再有意大利面条代码...) RuntimePermissionHe
3.7 更新命令用户接口(UI)消息 3.8 快捷菜单 第四课 工具条和状态栏 4.1 工具条的可视化设计 4.2 工具条的编程技术 4.3 状态栏的设计与实现 第五课 对话框 5.1对话框和控件的基本概念 5.2 ...
这种做法应该在其它显示UI的线程里效仿,因为它们都受相同的 超时影响。 IntentReceiver执行时间的特殊限制意味着它应该做:在后台里做小的、琐碎的 工作如保存设定或者注册一个Notification。和在主线程里调用的...
3.7 更新命令用户接口(UI)消息 3.8 快捷菜单 第四课 工具条和状态栏 4.1 工具条的可视化设计 4.2 工具条的编程技术 4.3 状态栏的设计与实现 第五课 对话框 5.1对话框和控件的基本概念 5.2 ...
3.7 更新命令用户接口(UI)消息 3.8 快捷菜单 第四课 工具条和状态栏 4.1 工具条的可视化设计 4.2 工具条的编程技术 4.3 状态栏的设计与实现 第五课 对话框 5.1对话框和控件的基本概念 5.2 ...
6.1.2.5 给菜单项添加UPDATE_COMMAND_UI 消息处理 6.1.2.6 一个简单的绘图程序 6.1.3 在应用程序中控制菜单 6.1.3.1 在应用程序中取得菜单 6.1.3.2 在应用程序中修改菜单的状态 6.1.3.3 在应用程序中添加、删除...
1.12.3 UI对象 20 1.13 小 结 20 第2章 控制条 21 2.1 通用控制条 21 2.2 用API创建控制条 22 2.3 用MFC创建控制条 24 2.3.1 CToolBarCtrl和CStatusBarCtrl 24 2.3.2 CToolBar和CStatusBar 24 2.3.3 CControlBar 26 ...
1.12.3 UI对象 20 1.13 小 结 20 第2章 控制条 21 2.1 通用控制条 21 2.2 用API创建控制条 22 2.3 用MFC创建控制条 24 2.3.1 CToolBarCtrl和CStatusBarCtrl 24 2.3.2 CToolBar和CStatusBar 24 2.3.3 CControlBar 26 ...
1.12.3 UI对象 20 1.13 小 结 20 第2章 控制条 21 2.1 通用控制条 21 2.2 用API创建控制条 22 2.3 用MFC创建控制条 24 2.3.1 CToolBarCtrl和CStatusBarCtrl 24 2.3.2 CToolBar和CStatusBar 24 2.3.3 CControlBar 26 ...
1.12.3 UI对象 20 1.13 小 结 20 第2章 控制条 21 2.1 通用控制条 21 2.2 用API创建控制条 22 2.3 用MFC创建控制条 24 2.3.1 CToolBarCtrl和CStatusBarCtrl 24 2.3.2 CToolBar和CStatusBar 24 2.3.3 ...
1.12.3 UI对象 20 1.13 小 结 20 第2章 控制条 21 2.1 通用控制条 21 2.2 用API创建控制条 22 2.3 用MFC创建控制条 24 2.3.1 CToolBarCtrl和CStatusBarCtrl 24 2.3.2 CToolBar和CStatusBar 24 2.3.3 CControlBar 26 ...
简单的UI模块,提供面板按钮和更多内容(正在建设中) CMake基础构建环境 讨论: 支持平台: Windows( ): Linux( ): 静态代码分析 覆盖范围状态: 入门 您需要安装什么? 具有或多或少现代GPULinux或...
提供有增强 GDI 函数,包括光栅操作、复杂区域处理、椭圆、圆弧、多边形以及区域填充等函数。 在提供有兼容于 C99 规范的数学库平台上,还提供有高级二维绘图函数。 通过 Light GUI 的图形抽象层及图形引擎技术,...
15. 在本地播放数据包中的视频流,可以先提取位图,再显示。由于系统没有提供直接播放的相关方法。 16. 线程同步的方法 – synchronized 17. F:\sipdroid\res\drawable 中的图标可以更换 18. sipdroid\res\values-...
9.21、位图旋转 227 9.22、手机震动控制 228 9.23、SENSOR2D感应实例 228 9.24、运用JAVA MAIL包实现发GMAIL邮件 230 9.26、ANDROID键盘响应 236 9.27、后台监听某个按键 238 9.28、VECTOR用法 239 9.29、CURSOR 242...
9.21、位图旋转 227 9.22、手机震动控制 228 9.23、SENSOR2D感应实例 228 9.24、运用JAVA MAIL包实现发GMAIL邮件 230 9.26、ANDROID键盘响应 236 9.27、后台监听某个按键 238 9.28、VECTOR用法 239 9.29、CURSOR 242...