处理来自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()方法中.