读书人

处置来自UI线程的位图

发布时间: 2013-07-09 09:50:48 作者: rapoo

处理来自UI线程的位图

?

 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627
   class BitmapWorkerTask extends {    private final ;    private int = 0;    public BitmapWorkerTask() {        // Use a WeakReference to ensure the ImageView can be garbage collected        = new ();    }    // Decode image in background.    @Override    protected doInBackground(... ) {        = [0];        return decodeSampledBitmapFromResource((), , 100, 100));    }    // Once complete, see if ImageView is still around and set bitmap.    @Override    protected void onPostExecute() {        if (null) {            final = .get();            if (!= null) {                .setImageBitmap();            }        }    } }

把ImageView设置成WeakReference,是因为这能够确保它所指向的ImageView和任何东西在垃圾回收时不被AsyncTask所阻止掉. 并不能保证ImageView在任务要结束时仍然存在,所以你必须在onPostExecute()方法中检查它的引用. ImageView可能不再存在了,例如,如果在任务要结束之前用户已经离开了当前Activity或者屏幕发生了旋转.

为了异步地加载位图,简单地创建一个新的任务并且执行它:

1234
  public void loadBitmap(int , ) {    = new ();    .execute(); }

处理并发

常见的视图组件例如ListView和GridView如在上一节中当和AsyncTask结合使用时引出了另外一个问题.为了优化内存,当用户滚动时这些组件回收了子视图.如果每个子视图触发一个AsyncTask,当它完成时没法保证,相关的视图还没有被回收时已经用在了别的子视图当中.此外,还有异步任务开始的顺序是不能保证他们完成的顺序.

这篇文章透过Multithreading for Performance功能讨论处理并发,并且提供了一个当任务完成后ImageView将一个引用存储到后面能被检查的AsyncTask的解决方案.使用类似的方法,从上一节的AsyncTask可以扩展到遵循类似的模式.

创建一个专用的Drawable的子类来存储一个引用备份到工作任务中.在这种情况下,一个BitmapDrawable被使用以便任务完成后一个占位符图像可以显示在ImageView中:

 1 2 3 4 5 6 7 8 91011121314
  static class AsyncDrawable extends {    private final ;    public AsyncDrawable(, ,            ) {        super(, );        =            new WeakReference();    }    public getBitmapWorkerTask() {        return .get();    } }

执行BitmapWorkerTask前,你创建一个AsyncDrawable,并将其绑定到目标ImageView:

123456789
    public void loadBitmap(int , ) {    if ((, )) {        final = new ();        final =                new AsyncDrawable((), , );        .setImageDrawable();        .execute();    } }

如果别的正在运行的任务已经和这个ImageView关联,cancelPotentialWork引用在上面的代码示例检查中.如果这样,它试图通过调用cancel())取消先前的任务.在少数情况下,新的任务数据匹配现有的任务,而且并不需要做什么.下面是实现 cancelPotentialWork:

 1 2 3 4 5 6 7 8 910111213141516
  public static boolean cancelPotentialWork(int , ) {    final = ();    if (!= null) {        final int = .data;        if (!= ) {            // Cancel previous task            .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 910
  private static getBitmapWorkerTask() {   if (!= null) {       final = .getDrawable();       if (instanceof ) {           final = () ;           return .getBitmapWorkerTask();       }    }    return null; }

这最后一步是在BitmapWorkerTask更新onPostExecute()方法,以便任务取消时并且当前任务和这个ImageView关联时进行检查:

 1 2 3 4 5 6 7 8 910111213141516171819
  class BitmapWorkerTask extends {    ...    @Override    protected void onPostExecute() {        if (()) {            = null;        }        if (!= null && != null ) {            final = .get();            final =                    ();            if (this == && != null) {                .setImageBitmap();            }        }    } }

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

读书人网 >移动开发

热点推荐