多线程处理Bitmaps


    上一篇,我们讨论了:Android有效的处理Bitmap,降低内存 ,可是最好不要运行在主线程(UI线程),假设图片是本地的或者网络的又或者是其它地方的。

图片载入的时间和很多因素有关(比方从网络或本地读取速度,图片的大小。CPU的能力),假设这些任务堵塞了UI线程,系统有可能会回收并关闭它(see Designing
for Responsiveness
 for more information).

   这篇我们将讨论怎样在UI线程之外后台使用异步任务(AsyncTask)加入Bitmap(本地或者网络图片或者其它资源的图片),而且叫你怎样并发处理他们。

使用异步任务

    异步任务提供了一种方便的方法来实现UI线程和后台线程的交互。我们能够派生一个类,而且重写他的方法。以下有个载入一张大图到ImageView的样例,使用上篇的降到的技术decodeSampledBitmapFromResource()(请看上篇的内容http://blog.csdn.net/liu8497/article/details/40786721):
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);
}
}
}
}

使用 WeakReference来保存ImageView为了内存可以非常好的回收他们,可是不能保证异步任务结束的时候。该ImageView还存在,所以必须在

onPostExecute()检查是否为空。该ImageView可能为空,比方在异步任务退出之前。当Activity退出的时候或者其它配置改变的时候
  启动载入一个Bitmap仅仅须要实例化一个任务而且运行它就可以:
public void loadBitmap(int resId, ImageView imageView) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(resId);
}

并行处理

    常规组件。比方ListView和GridView在与异步任务结合的时候引入了其它问题。

为了更好的使用内存,当用户滚动的时候就開始回收其它子View。假设每一个子View都使用一个异步任务。我们不能保证当异步任务结束的时候。相关联的view没有被回收掉。我们也不能保证异步任务依照我们的顺序运行。

  有一篇博客 Multithreading for Performance,深入的讨论了并行处理的问题,而且提供了一种解决方法。其ImageView指向了近期的一个异步任务,而且能够在任务结束之后检验运行。运用同样的方法,上面提到的异步任务能够遵循使用类似的路线。
  创建一个专用的Drawable来存储指向该任务的资源。所以,我们使用BitmapDrawable,这样在任务结束的时候我们的image就展示在ImageView里面:
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之前。我们能够给ImageView绑定一个AsyncDrawable。

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这种方法检查是否有其它任务关联到该ImageView。

假设是的话,我们使用函数cancel()取消掉关联的任务。

非常少情况下,新任务的数据与之前的匹配而且交互。以下是cancelPotentialWork的实现:

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相关的任务。

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()在BitmapWorkerTask更新为了检验任务是否取消而且检验当前任务是否匹配该ImageView:

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 的方法就能放一张图片到ImageVIew里面。

比方,在GridVIew里面,我们在Adapter的getView方法里面使用这样的方法。


关于Bitmap的优化。请看上篇:Android有效的处理Bitmap,降低内存

在UI线程之外,多线程处理Bitmaps的更多相关文章

  1. android 在UI线程之外处理Bitmap - 开发文档翻译

    由于本人英文能力实在有限,不足之初敬请谅解 本博客只要没有注明“转”,那么均为原创,转贴请注明本博客链接链接 Processing Bitmaps Off the UI Thread 在UI线程之外处 ...

  2. 【Android Developers Training】 57. 在UI线程之外处理图像

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  3. 在UI线程之外显示Toast

    new Thread(){ public void run() { Looper.prepare(); Toast t = Toast.makeText(mContext, R.string.cras ...

  4. C# 多线程详解 Part.02(UI 线程和子线程的互动、ProgressBar 的异步调用)

           我们先来看一段运行时会抛出 InvalidOperationException 异常的代码段: private void btnThreadA_Click(object sender, ...

  5. Android UI线程和非UI线程

    Android UI线程和非UI线程 UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这 ...

  6. 第10讲- UI线程阻塞及其优化

    第10讲UI线程阻塞及其优化 .UI 阻塞demo (首先在activity_main.xml中放置两个button,分别命名为button1,button2) //首先设置一个button1用来进行 ...

  7. android脚步---如何看log之程序停止运行,和UI线程和非UI线程之间切换

    经常运行eclipse时,烧到手机出现,“停止运行”,这时候得通过logcat查log了.一般这种情况属于FATAL EXCEPTION,所以检索FATAL 或者 EXCEPTION,然后往下看几行 ...

  8. Android ActivityThread(主线程或UI线程)简介

    1. ActivityThread功能 它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client ...

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

    我们在使用手机的时候,经常会遇到一个问题:先是卡死,然后跳出该程序无响应,是否关闭的提示(当然有可能是我们手机性能太差=.=)这是因为线程的阻塞引起的,在这里我讲述一下UI线程,一般处理程序会在UI线 ...

随机推荐

  1. background-position 之剑走偏锋

    转自:http://www.cnblogs.com/yizuierguo/archive/2009/03/10/1407860.html 在设置background-image属性时,经常会遇到一个b ...

  2. Shell 基本运算符(转)

    Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符 关系运算符 布尔运算符 字符串运算符 文件测试运算符 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 ...

  3. Springmvc UPDATE 数据时 ORA-01858:a non-numeric character was found where a numeric was expected

    ORA-01858:a non-numeric character was found where a numeric was expected 异常. 我的代码: 主要是绑定变量带出来的问题. 出错 ...

  4. To Use Genymotion

    Chinese Site:http://www.genymotion.cn/ Offical Site:http://www.genymotion.com/  Not available in Chi ...

  5. Android简单介绍

    1)Android定义: 2)版本号变迁 3)开发类型 4)Android五大组件 5)Android五大布局

  6. todos Vue

    <div id="todo-list-example"> <input v-model="newTodoText" v-on:keyup.en ...

  7. NetBeans 设置code completion/auto pop-up delay

    如果你在Tools>Options>Editor>Code Completion>Language: Java 没有找到设置delay的选项.那就去C盘(如果你用的是Windo ...

  8. 如何查看iis的连接数量

    引用:http://jingyan.baidu.com/article/54b6b9c0f3c2002d583b470d.html 运行,输入,perfmon.msc.   在系统监视器,区域点击,添 ...

  9. The Definitive Guide To Django 2 学习笔记(一) Views and UrL confsRL

    1.如何找到django在Ubuntu下的安装路径: 进入python命令行,import django,print(django.__path__) 2.使用django-admin.py 创建项目 ...

  10. iOS学习笔记10 - Bundle和Info.plist

    经常会有需要从应用中搜索并读取一个文件或图片.这时候就会用到如下的语句: NSString *path = [[NSBundle mainBundle] pathForResource:@" ...