文档章节

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

k
 kim366
发布于 2016/05/13 19:36
字数 1230
阅读 5
收藏 0
点赞 2
评论 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
【Google官方教程】第三课:缓存Bitmap

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

RyanHoo
2012/11/11
0
18
YYImage 源码剖析:图片处理技巧

引言 首先问一个问题:你会用图片么? 图片是现代化 APP 界面设计里应用广泛的东西,精美的图片可以带来视觉上的享受,提高用户体验。由此给技术上带来了一些挑战,比如动图的处理、图片显示...

indulge_in
07/16
0
0
【腾讯优测干货分享】使用多张图片做帧动画的性能优化

本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57fc8cea302e4725036142f6 使用多张图片做帧动画的性能优化 背景 QQ群的送礼物功能需要加载几十...

腾讯Bugly
2016/10/11
613
2
网页性能优化,缓存优化、加载时优化、动画优化

本文提供一个优化网页性能的大概思路,具体操作网上资料很多。 缓存优化 性能优化第一步,便是管理好页面的缓存,避免重复下载资源。否则,即增加服务器压力,又折磨用户的钱包。 浏览器缓存...

lunaqi
05/16
0
0
对"QQGame-大家来找茬"的辅助工具的改进

  【前言】最近在博客园首页上看到有“大家来找茬”这个游戏(此游戏为找出两个相近图片的不同点)外挂的相关帖子,所以这里我也翻看了我之前(2009年5月)的写的一个简单的辅助程序(采用...

hoodlum1980
2014/01/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

设计神器 - 摹客设计系统上线了 | 晒出你的设计规范,赢iPad Pro!

在国内,设计规范也许还是个不太常用的概念,但是如果你正好有参与互联网公司的产品设计,你应该早就已经体会到设计规范的重要性了。UI设计师总是要花费大量的时间和精力向开发描述一大堆设计...

mo311
9分钟前
0
0
Thymeleaf 使用过程中的一些记录

Thymeleaf格式化时间: th:value="${#dates.format(gw.regDT,'yyyy-MM-ddHH:mm:ss')}" Thymeleaf select反选: <select id="status" name="status" th:field="${gw.status}" th:value="${gw.......

惊尘大人
12分钟前
0
0
istio源码分析之pilot-discovery模块分析

本文分析的istio代码版本为0.8.0,commit为0cd8d67,commit时间为2018年6月18日。 本文为Service Mesh深度学习系列之一: Service Mesh深度学习系列part1—istio源码分析之pilot-agent模块分...

xiaomin0322
17分钟前
0
0
数据库基本操作:增删改查及联表操作

所用软件:SQL Server Management Studio 首先第一步,建立一个表。在这里命名为T1。并在里面填入几条数据。如图: T1 一.查询 查询所有:select * from T1; 按条件查询:select * from T1 ...

小_橙_子
21分钟前
0
0
Crontab作业时间设置

今天,遇到这么一个题目,周一到周五的9:00-16:59之间,每隔两分钟将某个命令运行一次。给的答案是: */2 9-16 * * 1-5 /usr/sbin/somecommand dosomething 乍一看,这个答案不对,应...

大别阿郎
26分钟前
0
0
ES17-JAVA API文档管理

1.保存文档 可以通过json工具把java对象转换成json字符串进行保存,也可以通过内置的帮助类直接构建json格式 /** * 获取客户端 * * @return */public static TransportClie...

贾峰uk
27分钟前
0
0
Python代码规范和命名规范

前言 Python 学习之旅,先来看看 Python 的代码规范,让自己先有个意识,而且在往后的学习中慢慢养成习惯 一、简明概述 1、编码 如无特殊情况, 文件一律使用 UTF-8 编码 如无特殊情况, 文件头...

blackfoxya
30分钟前
0
0
联动滑动之一:NestScrollChild和NestedScrollingParent

NestScrollChild和NestedScrollingParent 吐槽一下开源中国竟然标题字数有限制 由于项目中使用了CoordinateLayout来解决联动以及实现炫酷的UI效果,那么必须就要研究一波源码了,毕竟知其然知...

JerryLin123
47分钟前
1
0
cloudera spark2.2 读写hbase

cloudera spark2.2 读写hbase 例子 host = 'bigdata-03,bigdata-05,bigdata-04'conf = { "hbase.zookeeper.quorum": host, "hbase.mapreduce.inputtable": "student1"}k......

osenlin
51分钟前
0
0
数据库规范化

转载自 一个小时学会MySQL数据库 地址:http://www.cnblogs.com/best/p/6517755.html 截取其中 1.4 部分 用于自己学习使用 感谢作者:张果 1.4、数据库规范化 经过一系列的步骤,我们现在终于...

十万猛虎下画山
52分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部