`

处理来自UI线程的位图

阅读更多

 

原文自: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);
            }
        }
    }
 }

现在这个实现适合使用ListViewGridView控件组件以及回收其子视图的任何其他组件.在你正常地给你的ImageView控件设置图片时简单地调用loadBitmap就行了.例如,在一个GridView中实现的方式是在支持的适配中的android.view.View, android.view.ViewGroup) getView()方法中.

0
1
分享到:
评论

相关推荐

    bitmap-example

    示例演示如何从主 UI 线程有效地加载大位图、缓存位图(在内存和磁盘中)、管理位图内存以及在 UI 元素(如 ViewPager 和 ListView/GridView)中显示位图。 介绍 这是 Android Training 类的示例应用程序 。 它演示...

    AndroidMediaDB:此代码取自 Android 中的 DisplayingBitmaps Sample。 它显示Android手机上的所有图像和视频,组织到文件夹中(与图库应用程序相同)

    Android 媒体数据库示例(原始 DisplayingBitmaps 示例 ( ))当前版本 1.0 Beta 示例演示如何从主 UI 线程有效地从 Android sd 卡(图库)加载大型位图、缓存位图(在内存和磁盘中)、管理位图内存并在 UI 元素 - ...

    WTL起步-玩转图形界面

    第六章 创建多线程SDI应用程序 19 第七章 创建MDI应用程序 23 第八章 分隔窗口 26 第九章 GDI的封装 31 第十章 CString及其它 34 第十一章 动态数据交换(DDX)的WTL支持 35 第十二章 WTL向导 37 第十三章 WTL例程 ...

    BackgroundTask:简单的 Android 库,在某些情况下可以替换 AsyncTask(请参阅自述文件)

    后台任务 这个 Android 库引入了一些使用线程的React性...您想从源中获取一些“结果对象”——来自 url 的位图对象 然后你创建 SourceHandler<String> 对象,它可以从 url 制作图像 您创建 ResultHandler 对象,您可以

    android-helpers:Android的帮助器类的集合

    帮手这是Android的帮助... 对于(在接下来的UI线程循环即引起重新布局,而不是现在的)使用视图助手ActivityResultHelper用于使用结果代码简化开始活动(MainActivity中不再有意大利面条代码...) RuntimePermissionHe

    VC++可视化编程指南

     3.7 更新命令用户接口(UI)消息  3.8 快捷菜单 第四课 工具条和状态栏  4.1 工具条的可视化设计  4.2 工具条的编程技术  4.3 状态栏的设计与实现 第五课 对话框  5.1对话框和控件的基本概念  5.2 ...

    andriod精华学习教程

    这种做法应该在其它显示UI的线程里效仿,因为它们都受相同的 超时影响。 IntentReceiver执行时间的特殊限制意味着它应该做:在后台里做小的、琐碎的 工作如保存设定或者注册一个Notification。和在主线程里调用的...

    VC++ 编程指南_中文chm

     3.7 更新命令用户接口(UI)消息  3.8 快捷菜单 第四课 工具条和状态栏  4.1 工具条的可视化设计  4.2 工具条的编程技术  4.3 状态栏的设计与实现 第五课 对话框  5.1对话框和控件的基本概念  5.2 ...

    VC++编程指南(中文版).chm

     3.7 更新命令用户接口(UI)消息  3.8 快捷菜单 第四课 工具条和状态栏  4.1 工具条的可视化设计  4.2 工具条的编程技术  4.3 状态栏的设计与实现 第五课 对话框  5.1对话框和控件的基本概念  5.2 ...

    MFC教程入门知识全集.rar

    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 在应用程序中添加、删除...

    VISUAL C++MFC扩展编程实例(想学MFC的朋友一定不要错过)

    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 ...

    一份很实用的MFC资料

    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 ...

    Visual C++ MFC扩展编程实例.PDF

    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 ...

    VISUAL C MFC扩展编程实例与源码

    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 ...

    VISUAL C++MFC扩展编程实例

    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 ...

    osre:一个开源渲染引擎

    简单的UI模块,提供面板按钮和更多内容(正在建设中) CMake基础构建环境 讨论: 支持平台: Windows( ): Linux( ): 静态代码分析 覆盖范围状态: 入门 您需要安装什么? 具有或多或少现代GPULinux或...

    LightGUI一个轻量级的GUI

    提供有增强 GDI 函数,包括光栅操作、复杂区域处理、椭圆、圆弧、多边形以及区域填充等函数。 在提供有兼容于 C99 规范的数学库平台上,还提供有高级二维绘图函数。 通过 Light GUI 的图形抽象层及图形引擎技术,...

    SipDroid客户端源码

    15. 在本地播放数据包中的视频流,可以先提取位图,再显示。由于系统没有提供直接播放的相关方法。 16. 线程同步的方法 – synchronized 17. F:\sipdroid\res\drawable 中的图标可以更换 18. sipdroid\res\values-...

    Android 开发技巧

    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...

    Android开发资料合集-World版!

    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...

Global site tag (gtag.js) - Google Analytics