文档章节

位图管理、图片下载缓存、管理图片内存 (三) 在非UI线程中处理位图

k
 kim366
发布于 2016/05/13 19:36
字数 1230
阅读 5
收藏 0

        BitmapFactory.decode*等解码方法不应在主线程中执行,假如资源数据是从硬盘或者网络地址中读取的话(或者说除内存以外的其他任意位置)。这些数据可能花费的时间是不可预知的,依赖于一系列的因素(包括硬盘或者网络的读取速度,图片尺寸,CPU处理能力等)。如果其中某个因素阻塞了UI线程,可能导致应用提示无响应状态。本节将学习如何通过AsyncTask在后台处理位图,并说明如何处理并发问题。


        使用异步任务
        异步任务类提供了一种简单的方法,用于在后台线程中执行某些工作,并将结果发布到UI线程中。要使用异步任务,需要创建AsyncTask的子类,并重写其中一些方法,下面是一个通过AsyncTask下载大图到ImageView中的实例。

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可以让AsyncTask不阻止ImageView和其他的资源被垃圾回收,不过,当异步任务结束时不保证ImageView仍然存在,所以必须在onPostExecute()中检查引用。ImageView可能已经不存在,比如说,客户从当前Activity中导航离开,或者在异步任务结束之前配置发生改变(屏幕方向发生改变)。
        异步下载位图只需要创建一个该异步任务的子类,并执行即可。


        处理并发
        如上一节所示,常用组件如ListView和GridView与异步任务结合使用时可能引起另外一个问题,为了高效使用内存,当组件滚动时,组件会循环使用其子视图,如果每个视图都触发一个AsyncTask,则无法确定当他们结束时,与之相关联的视图是否正被另外一个视图循环使用着。而且,无法保证异步任务启动执行的顺序与其结束执行的顺序一致。

        这篇博客对多线程操作的并发问题作进一步讨论,并提供一种方案用于在AsynTask中存贮一个最近使用的ImageView的引用,这个引用会在异步任务结束时被再次检查。通过相似的方法,上一节中讨论的异步任务可以被扩展至如下一种相似的模式。
        创建一个专用的Drawable子类用于存储一个引用到工作task中,这样,一个BitmapDrawable会被使用,当这个异步任务结束时,其持有的图片会被展示到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之前你可以创建一个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 方法会检查是否有另外一个异步任务已经跟这个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 != 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;
}
        最后一步是在BitmapWorkerTask中更新onPostExecute()方法,检查当前异步任务是否已经被取消,以及是否当前异步任务与当前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即可。例如在GridView的实现类中,这个过程只要在后台的适配器中执行即可。


本文转载自:http://blog.csdn.net/oyangyujun/article/details/41039125

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
【Google官方教程】前言:高效的Bitmap显示

转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) http://my.oschina.net/ryanhoo/blog/88153 译者:Ryan Hoo 来源:https://developer.andro...

RyanHoo
2012/11/09
0
11
Android图片加载库Glide和Fresco是如何工作的

原文地址:https://blog.mindorks.com/how-the-android-image-loading-library-glide-and-fresco-works-962bc9d1cc40 通常,我们在加载图片的时候经常会遇到如下的问题: 内存溢出错误 图片加...

尺锤
2017/09/16
0
0
【Google官方教程】第四课:在UI中显示Bitmap

转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) http://my.oschina.net/ryanhoo/blog/88484 译者:Ryan Hoo 来源:https://developer.andro...

RyanHoo
2012/11/11
0
3
FastImageCache 架构分析

原文 文章介绍 本文章注重分析 FastImageCache 这个 Github 第三方图片IO库的架构和部分分析等等。 对于 FastImageCache 很多同学或多或少都会听过,但是网上很多人说这是一个网络图片库,我...

葱神大大
06/22
0
0
Android中的缓存处理

一、缓存介绍 (一)、Android中缓存的必要性: 1、没有缓存的弊端: 流量开销:对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量。 ...

墨宇hz
2015/04/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

白话SpringCloud | 第十章:路由网关(Zuul)进阶:过滤器、异常处理

前言 简单介绍了关于Zuul的一些简单使用以及一些路由规则的简单说明。而对于一个统一网关而言,需要处理各种各类的请求,对不同的url进行拦截,或者对调用服务的异常进行二次处理等等。今天,...

oKong
19分钟前
1
0
详解nohup和& 区别

详解nohup和& 区别 2017年11月29日 16:57:38 King-Long 阅读数:7266 版权声明:本文为博主原创文章,欢迎转载。 https://blog.csdn.net/u011095110/article/details/78666833 nohup 一、【解...

linjin200
31分钟前
3
0
Character的static方法

基本类型char的包装类是Character,使用的比较多,大家是比较熟悉的。 我只是觉得里面有很多static方法,平时不怎么用,学习一下怎么实现的,或许日后就用到了。 static int compare(char x,...

woshixin
31分钟前
1
0
正则介绍_sed

10月17日任务 9.4/9.5 sed sed工具 匹配打印 -n 只打印匹配行,不然其他行也会打印出来 p 打印(配合-n使用) [root@centos7 tmp]# sed -n '/root/'p passwd root:x:0:0:root:/root:/bin/ba...

robertt15
31分钟前
2
0
轻松解码类似eval(function(p,a,c,k,e,d){}))的JavaScript代码

轻松解码类似eval(function(p,a,c,k,e,d){}))的JavaScript代码 2013年05月22日 15:18:05 秋实先生 阅读数:14826 这里解码百度访问统计代码构造函数为示例: 百度访问统计代码JavaScript源码:...

_纵横捭阖_
40分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部