在UI线程外处理Bitmap

使用AsyncTask

AsyncTask提供了一个方便的方法,你可以在一个后台线程中进行某些工作 ,然后把结果展现在UI线程中。

为了正确的使用AsyncTask类,以下是几条必须遵守的准则:

  1) Task的实例必须在UI 线程中创建

  2) execute方法必须在UI 线程中调用

  3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法,需要在UI线程中实例化这个task来调用。

  4) 该task只能被执行一次,否则多次调用时将会出现异常

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以及它所关联的任何引用的垃圾回收。不保证在task完成的时候ImageView仍然是存在的(没有被回收),所以必须要在onPostExecute()方法中检查是否为空。ImageView可能不会一直存在,例如,如果用户离开了当前的Activiy,或者在task完成前屏幕发送了旋转等。为了能够异步的开始加载bitmap,简单的创建一个先的task并开始执行:

public void loadBitmap(int resId, ImageView imageView) {    BitmapWorkerTask task = new BitmapWorkerTask(imageView);    task.execute(resId);}

处理并发

像ListView和GridView这种常用的View当使用AsyncTask的时候会存在另一个问题。为了更加有效的使用内存,这些组件会在用户滑动的时候回收子view。如果每个子view都启用了一个AsyncTask,就不能保证当这个task完成的时候,它对应的子view没有被回收,并且在用在了另一个子view中。而且也不能保证,开始这些task的顺序就是这些task完成的顺序。

关于上述问题会在Multithreading for Performance一文中进行介绍。并提供一个解决方法:ImageView存储了最近执行的一个AsyncTask的实例引用,这个AsyncTask可以在稍后被检查什么时候执行完成。

创建一个专用的Drawable的子类来存储task的一个引用。这样,

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()方法检查了是否有另一个运行中的task关联了这个ImageView。如果是,它会尝试调用cancle()方法取消前面的task:

public static boolean cancelPotentialWork(int data, ImageView imageView) {    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

    if (bitmapWorkerTask != null) {        final int bitmapData = bitmapWorkerTask.data;        // If bitmapData is not yet set or it differs from the new data        if (bitmapData == 0 || 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相关的task:

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;}

最后一步就是在onPostExecute()方法中进行更新:

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这种控件中了。只要简单的调用loadBitmap方法就行了。例如,有一个GridView应该在它的Adapter的getView()方法中使用loadBitmap()方法。

时间: 2024-10-01 00:42:22

在UI线程外处理Bitmap的相关文章

【转载】Android中UI线程与后台线程交互设计的5种方法

原帖地址:http://www.cr173.com/html/19165_1.html 在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须要重新开启一个后台线程运行这些任务.然而,往往这些任务最终又会直接或者间接的需要访问和控制UI控件.例如访问网络获取数据,然后需要将这些数据处理显示出来.就出现了上面所说的情况.原本这是在正常不过的现象了,但是android规定除了UI线程外,其他线程都不可以对那些UI控件访问

Android中UI线程与后台线程交互设计的6种方法

在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须要重新开启 一个后台线程运行这些任务.然而,往往这些任务最终又会直接或者间接的需要访问和控制UI控件.例如访问网络获取数据,然后需要将这些数据处理显示出来. 就出现了上面所说的情况.原本这是在正常不过的现象了,但是android规定除了UI线程外,其他线程都不可以对那些UI控件访问和操控.为了解决这个 问题,于是就引出了我们今天的话题.Android中后台线程如何

Android Studio学习随笔-UI线程阻塞以及优化

我们在使用手机的时候,经常会遇到一个问题:先是卡死,然后跳出该程序无响应,是否关闭的提示(当然有可能是我们手机性能太差=.=)这是因为线程的阻塞引起的,在这里我讲述一下UI线程,一般处理程序会在UI线程中执行耗时操作,这回导致UI线程阻塞,当UI线程阻塞,屏幕会出现卡死,用户体验会变得非常差,当线程阻塞超过5s,android系统可能进行干预,弹出对话框询问是否关闭.那如何解决呢? 解决方案一:创建一个新线程 我在UI视图中创建了一个button和一个textView Button button

Android中UI线程与后台线程交互设计的5种方法

我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一 些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须要重新开启一个后台线程运行这些任务.然而,往往这些任务最终又会直接或者 间接的需要访问和控制UI控件.例如访问网络获取数据,然后需要将这些数据处理显示出来.就出现了上面所说的情况.原本这是在正常不过的现象了,但是 android规定除了UI线程外,其他线程都不可以对那些UI控件访问和操控.为了解决

非UI线程处理Bitmap

在上一课中有介绍一系列的BitmapFactory.decode*) 方法,当数据源是网络或者是磁盘时(或者是任何实际源不在内存的),这些方法都不应该在main UI 线程中执行.那些情况下加载数据是不可以预知的,它依赖于许多因素(从网络或者硬盘读取数据的速度, 图片的大小, CPU的速度, etc.).如果其中任何一个任务卡住了UI thread, 系统会出现ANR的错误. 这一节课会介绍如何使用 AsyncTask 在后台线程中处理bitmap并且演示了如何处理并发(concurrency)

Android自定义组件系列【12】——非UI线程绘图SurfaceView

一.SurfaceView的介绍 在前面我们已经会自定义View,使用canvas绘图,但是View的绘图机制存在一些缺陷. 1.View缺乏双缓冲机制. 2.程序必须重绘整个View上显示的图片,比较耗资源. 3.非UI线程无法更新View组件,所以会占用主线程资源,当需要在主线程中处理逻辑的时候会很慢. 在Android中为我们提供了一个SurfaceView来替代View实现绘制图形,一般在游戏绘图方面应用较广,所以如果是比较复杂的绘图建议使用SurfaceView. 二.SurfaceV

Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面

Android应用的开发过程中需要把繁重的任务(IO,网络连接等)放到其他线程中异步执行,达到不阻塞UI的效果. 下面将由浅入深介绍Android进行异步处理的实现方法和系统底层的实现原理. 本文介绍Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面: 即如何使用Thread+Handler的方式从非UI线程发送界面更新消息到UI线程. 概述:每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),

Android UI线程和非UI线程

UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components from the Android UI toolkit (components from the android.widget and android.view packages))发生交互. 所以main thread也叫UI thread也即UI线程. 系统不会为

在UI线程之外,多线程处理Bitmaps

多线程处理Bitmaps 上一篇,我们讨论了:Android有效的处理Bitmap,降低内存 ,可是最好不要运行在主线程(UI线程),假设图片是本地的或者网络的又或者是其它地方的. 图片载入的时间和很多因素有关(比方从网络或本地读取速度,图片的大小.CPU的能力),假设这些任务堵塞了UI线程,系统有可能会回收并关闭它(see Designing for Responsiveness for more information). 这篇我们将讨论怎样在UI线程之外后台使用异步任务(AsyncTask