转载:http://my.oschina.net/rengwuxian/blog/182885

高效使用Bitmaps有什么好处?

我们常常提到的“Android程序优化”,通常指的是性能和内存的优化,即:更快的响应速度,更低的内存占用。Android程序的性能和内存问题,大部分都和图片紧密相关,而图片的加载在很多情况下很用到Bitmap(位图)这个类。而由于Bitmap自身的特性(将每个像素的属性全部保存在内存中),导致稍有不慎就会创建出一个占用内存非常大的Bitmap对象,从而导致加载过慢,还会有内存溢出的风险。所以,Android程序要做优化,Bitmap的优化是必不可少的一步。

需要对Bitmap进行优化的情形

首先请看一行代码:

1
mImageView.setImageResource(R.drawable.my_image);

这是一行从资源文件中加载图片到ImageView的代码。通常这段代码没什么问题,但有些情况下,你需要对这段代码进行优化。例如当图片的尺寸远远大于ImageView的尺寸时,或者当你要在一个ListView或GridView中批量加载一些大小未知的图片时。实际上,以上这行代码会在运行时使用BitmapFactory.decodeStream()方法将资源图片生成一个Bitmap,然后由这个Bitmap生成一个Drawable,最后再将这个Drawable设置到ImageView。由于在过程中生成了Bitmap,因此如果你使用的图片过大,就会导致性能和内存占用的问题。另外,需要优化的情形不止这一种,这里就不再列举。

下面分步说明使用代码来减小Bitmap的尺寸从而达到减小内存占用的方法:

1. 获取原图片尺寸

通常,我们使用BitmapFactory.decodeResource()方法来从资源文件中读取一张图片并生成一个Bitmap。但如果使用一个BitmapFactory.Options对象,并把该对象的inJustDecodeBounds属性设置为true,decodeResource()方法就不会生成Bitmap对象,而仅仅是读取该图片的尺寸和类型信息:

1
2
3
4
5
6
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

2. 根据原图尺寸和目标区域的尺寸计算出合适的Bitmap尺寸

BitmapFactory.Options类有一个参数inSampleSize,该参数为int型,他的值指示了在解析图片为Bitmap时在长宽两个方向上像素缩小的倍数。inSampleSize的默认值和最小值为1(当小于1时,解码器将该值当做1来处理),且在大于1时,该值只能为2的幂(当不为2的幂时,解码器会取与该值最接近的2的幂)。例如,当inSampleSize为2时,一个2000*1000的图片,将被缩小为1000*500,相应地,它的像素数和内存占用都被缩小为了原来的1/4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // 原始图片的宽高
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;
 
    if (height > reqHeight || width > reqWidth) {
 
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;
 
        // 在保证解析出的bitmap宽高分别大于目标尺寸宽高的前提下,取可能的inSampleSize的最大值
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }
 
    return inSampleSize;
}

3. 根据计算出的inSampleSize生成Bitmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {
 
    // 首先设置 inJustDecodeBounds=true 来获取图片尺寸
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);
 
    // 计算 inSampleSize 的值
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
 
    // 根据计算出的 inSampleSize 来解码图片生成Bitmap
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

这里有一点要注意,就是要在第二遍decode之前把inJustDecodeBounds设置回false。

4. 调用以上的decodeSampledBitmapFromResource方法,使用自定尺寸的Bitmap

如果你要将一张大图设置为一个100*100的缩略图,执行以下代码:

1
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

到此,使用decodeResource()方法将一个大图解析为小尺寸bitmap的应用就完成了。同理,还可以使用decodeStream(),decodeFile()等方法做相同的事,原理是一样的。

延伸:一个Bitmap到底占用多大内存?系统给每个应用程序分配多大内存?

· Bitmap占用的内存为:像素总数 * 每个像素占用的内存。在Android中,Bitmap有四种像素类型:ARGB_8888、ARGB_4444、ARGB_565、ALPHA_8,他们每个像素占用的字节数分别为4、2、2、1。因此,一个2000*1000的ARGB_8888类型的Bitmap占用的内存为2000*1000*4=8000000B=8MB。

· Android根据设备屏幕尺寸和dpi的不同,给系统分配的单应用程序内存大小也不同,具体如下表(表格取自Android 4.4 Compatibility Definition Document (CDD)):

 屏幕尺寸  DPI  应用内存
 small / normal / large  ldpi / mdpi  16MB
 small / normal / large  tvdpi / hdpi  32MB
 small / normal / large  xhdpi  64MB
 small / normal / large  400dpi  96MB
 small / normal / large  xxhdpi  128MB
 xlarge  mdpi  32MB
 xlarge  tvdpi / hdpi  64MB
 xlarge  xhdpi  128MB
 xlarge  400dpi  192MB
 xlarge  xxhdpi  256MB

高效使用Bitmaps(一) 大Bitmap的加载的更多相关文章

  1. 高效使用Bitmaps(二) 后台加载Bitmap

    转载:http://my.oschina.net/rengwuxian/blog/183802 为什么要在后台加载Bitmap? 有没有过这种体验:你在Android手机上打开了一个带有含图片的Lis ...

  2. Android开发艺术探索》读书笔记 (12) 第12章 Bitmap的加载和Cache

    第12章 Bitmap的加载和Cache 12.1 Bitmap的高速加载 (1)Bitmap是如何加载的?BitmapFactory类提供了四类方法:decodeFile.decodeResourc ...

  3. Bitmap的加载和Cache

    由于Bitmap的特殊性以及Android对单个应用所施加的内存限制,比如16M,这导致加载Bitmap的时候很容易出现内存溢出.比如以下场景: java.lang.OutofMemoryError: ...

  4. Bitmap的加载与缓存

    Android系统中图片一般用Bitmap对象表示,它支持png,jpg等常见格式.通常情况下图片的体积都比较大,单个应用允许使用的内存又是有限的,所以我们需要采取一些手段减少内存占用并提高加载速度. ...

  5. vue单页面打包文件大?首次加载慢?按需加载?是你打开方式不对

    部署各vue项目,走了一遍坑.... vue单页面应用刷新404 找到nginx多网站配置文件:类似nginx/sites-available/www.baidu.com server { liste ...

  6. 《android开发艺术探索》读书笔记(十二)--Bitmap的加载和Cache

    接上篇<android开发艺术探索>读书笔记(十一)--Android的线程和线程池 No1: 目前比较常用的缓存策略是LruCache和DiskLruCache,LruCache常被用作 ...

  7. nginx配置解决vue单页面打包文件大,首次加载慢的问题

    cnpm run build 文件过大,其中主要是vender.js有1.5M,代码部署到服务器,首次访问加载页面时比较慢,耗时6.5s左右,所以需要优化下. 1.Nginx开启gzip 找到ngin ...

  8. javascript innerHTML 大数据量加载 导致IE 内存溢出 的解决办法

    在做 ajax 滚动加载的时候,越到后面 数据量越大,使用obj.innerHTML+=row添加到页面的时候,出现ie内存不足的情况,此时使用createDocumentFragment,创建一个文 ...

  9. cocos2dx 大地图分块加载的研究(初)

    http://blog.csdn.net/dinko321/article/details/46739563 http://blog.csdn.net/u012812482/article/detai ...

随机推荐

  1. [Unity菜鸟] Unity发布web后,从HTML调用本地文件

    1. 遇到的问题 从xml读数据,xml的编码用中文就会乱码,改编码格式就调用不了.目前采用默认的ANSI编码. .exe    .txt  .xlsx  .xml  .test Run     √ ...

  2. 省市区 Mysql 数据库表

    1.查省SELECT * FROM china WHERE china.Pid=02.查市SELECT * FROM chinaWHERE china.Pid=3300003.查区SELECT * F ...

  3. 关于Apache Struts 2 S2-032高危漏洞的一些确认

    2016年4月21日Struts2官方发布两个CVE,其中CVE-2016-3081(S2-032)官方评级为高. 主要原因为在用户开启动态方法调用的情况下,会被攻击者实现远程代码执行攻击. 具体的漏 ...

  4. memcache简易教程

    1.  memcache是什么 memcache是一个高性能的分布式的内存对象缓存系统,用于动态Web应用以减轻数据库负担.它通过在内存中缓存数据和对象,来减少读取数据库的次数.从而提高动态.数据库驱 ...

  5. CentOS7.1 Xshell 经常掉线 Connection closed by foreign host

    XShell如果经常对CentOS掉线,则VNC肯定连接不上 但是ping CentOS的IP又能ping通,主要原因还是因为sshd的设置问题 #进入ssh目录 cd /etc/ssh #修改ssh ...

  6. tcp断开的4次挥手

    http://blog.csdn.net/fw0124/article/details/7452695 由于TCP连 接是全双工的,因此每个方向都必须单独进行关闭.这原则是当一方完成它的数据发送任务后 ...

  7. Android开发之parseSdkContent failed Could not initialize class android.graphics.Typeface

    在进行android开发过程中,忽然发现经常弹出来parseSdkContent failed 这个错误,然后google了下解决办法 方法1: 删除.android文件 重启eclipse. 该方法 ...

  8. python 控制vbox虚拟机

    (本机环境:win7:python 2.7.9: vbox 4.3.12) 目标: 完成官网(https://pypi.python.org/pypi/pyvbox)上的demo: > pyth ...

  9. chrome禁用某个网站js脚本的执行

      1 首先打开谷歌浏览器.如下 2 点击右上角,打开菜单进入[设置] 3 打开后,第一个界面是没有这个的,要滚动到最后点击[显示高级设置...] 4 展开第二页后,点击[隐私设置]->[内容设 ...

  10. 十个拥有丰富UI组件的JS开发框架

    如今,网上有各种各样的 JavaScript 框架用来简化 Web 应用开发.这些框架都提供了一些核心的特性,例如 DOM 操作,动画,事件处理以及 Ajax 交互,但不是都带有 UI 组件.今天这篇 ...