原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/index.html

引言

学习如何使用一种常规的手段来处理及加载Bitmap对象,这种方式除了使用户界面是可响应的之外,还会避免超出内存的限制。如果你不小心点的话,位图会迅速的将那些可怜的内存消耗殆尽,并会导致程序崩溃,因为这会产生一种可怕的异常:

java.lang.OutofMemoryError: bitmap size exceeds VM budget.

这里列举出了一些原因来说明为什么加载位图对于Android程序来说是非常棘手的:

  • 移动设备通常含有有限的资源。Android设备对于单个程序只有少量的16MB可用内存。虚拟机兼容性(Virtual Machine Compatibility)针对于各种的屏幕尺寸和密度给出了最低限度的程序内存要求。程序应该在极小的内存空间下充分利用内存空间。无论如何要记住一点,很多设备配备了更高的限制。
  • 位图通常会消耗掉不少内存,尤其是丰富的图片,就像照片这样的。举个例子,Galaxy Nexus上的相机拍的照片会达到2592x1936个像素(五百万像素)。如果位图配置使用的是ARGB_8888(这在Android 2.3以前是默认的),那么加载这张照片到内存中就需要花费掉19MB的内存(2592*1936*4个字节),这会立即耗尽某些设备上的所有内存。
  • Android的APP界面有时会很频繁的请求一些图片来加载。有些组件比如ListView, GridView及ViewPager,它们有个共同的特性就是需要同时在屏幕上加载多个位图并会在屏幕之外的地方加载以便在手指滑动的时候显示出来。

有效加载大图

图片会有各种形状和大小。在很多情况下它们会比用户界面上所要求的尺寸要大。举个例子,系统的相册应用所展示的用相机拍摄的照片的分辨率通常要比屏幕的密度要高。

鉴于在有限的内存中工作,理想上只用加载低分辨率的版本就可以。低分辨率的版本应该匹配到展示这张图片的控件大小。图片的更高分辨率不会在视觉上有更佳的效果,但是这仍然会消耗宝贵的内存空间,由于额外的动态扩展,这会招致额外的性能开销。

这节课会讨论将大位图进行二次采样并将采样后的小版本加载到内存中的过程。这个过程并不会超出应用的内存限制。

读取位图的尺寸及类型

类BitmapFactory提供了若干个解码方法(decodeByteArray(), decodeFile(), decodeResource(), etc.)根据不同的资源来创建位图Bitmap。选择更加适合的解码方法取决于图片的数据资源。这些方法会在构造位图时尝试向内存申请空间,所以会轻易的造成OutOfMemory异常。每个解码方法都有一个附属特征,这个特征可以使你通过BitmapFactory.Options类来指定解码选项。设置inJustDecodeBounds属性为true可以避免在解码时向内存申请空间,这会返回一个空的位图,但是outWidth、outHeight和outMimeType这些设置除外。这项技术可以使你在构造位图(申请内存)之前提前读取图像数据的尺寸及类型。

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;

为了避免java.lang.OutOfMemory异常,需要在解码图片之前检查图片的尺寸,除非你对这些图像数据的尺寸绝对的信任,并且该尺寸对可用内存非常适用。

加载等比缩小的版本到内存

那么现在图片的尺寸是知道了,这尺寸可以被用来决定:是否全尺寸的图像应该被加载到内存中还是应该有个二次采样的版本加载到内存中。这里有一些因素需要考虑:

  • 往内存中加载全尺寸的图像应该估算要使用的内存大小。
  • 要加载的图片所需要的内存数量需要给应用预留一定的内存空间,不要消耗完全。
  • ImageView或者UI组件的尺寸是图像将要加载的尺寸。
  • 当前设备的屏幕尺寸与密度。

举个例子,加载一个1024*768像素的图片到内存中是没有价值的,如果这个图片最终被显示为一个128x96像素的缩略图的话。

为了告诉解码器需要进行二次采样,以便加载一个小版本的图像到内存中,需要设置BitmapFactory.Options对象的inSampleSize属性为true。举个例子,一张图片的分辨率为2048x1536,需要通过inSampleSize解码为4分之一大小的位图,大概是512x384。加载这样的图像只需要花费0.75MB内存,而全尺寸的图像则需要花费12MB的内存(假设位图的配置为ARGB_8888)。这里有一个方法可以来计算一个样本容量值,这个值是2的幂次方值并基于原图像的高度值与宽度值进行计算。

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {// Raw height and width of imagefinal 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;// Calculate the largest inSampleSize value that is a power of 2 and keeps both// height and width larger than the requested height and width.while ((halfHeight / inSampleSize) > reqHeight&& (halfWidth / inSampleSize) > reqWidth) {inSampleSize *= 2;}}return inSampleSize;
}

Note: 最终计算后的值是一个2的幂次方值是因为解码器需要通过舍入来获得一个最终值,这个值与2的幂次方最为接近,依据inSampleSize文档。

为了使用这个方法,第一步需要将inJustDecodeBounds设置为true,然后将options交给BitmapFactory使用,然后再次使用一个新的inSampleSize和inJustDecodeBounds设置为false来再次使用:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,int reqWidth, int reqHeight) {// First decode with inJustDecodeBounds=true to check dimensionsfinal BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(res, resId, options);// Calculate inSampleSizeoptions.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);// Decode bitmap with inSampleSize setoptions.inJustDecodeBounds = false;return BitmapFactory.decodeResource(res, resId, options);
}

这个方法可以很轻易的加载任何大尺寸的位图给ImageView,这个ImageView展示了一个100*100像素的缩略图,就像下面的代码所展示的这样:

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

你可以遵循类似的过程来对其它资源进行解码,如果需要的话,可以替代使用合适的BitmapFactory.decode*方法。

Android官方开发文档Training系列课程中文版:高效显示位图之加载大位图相关推荐

  1. Android官方开发文档Training系列课程中文版:目录

    原文地址 : http://android.xsoftlab.net/training/index.html 引言 在翻译了一篇安卓的官方文档之后,我觉得应该做一件事情,就是把安卓的整篇训练课程全部翻 ...

  2. Android官方开发文档Training系列课程中文版:创建自定义View之View的创建

    原文地址:http://android.xsoftlab.net/training/custom-views/index.html 引言 Android框架含有大量的View类,这些类用来显示各式各样 ...

  3. Android官方开发文档Training系列课程中文版:OpenGL绘图之图形绘制

    原文地址:http://android.xsoftlab.net/training/graphics/opengl/draw.html 如果你还不清楚如何定义图形及坐标系统,请移步:Android官方 ...

  4. Android官方开发文档Training系列课程中文版:使用Fragment构建动态UI之Fragment创建

    原文地址:http://android.xsoftlab.net/training/basics/fragments/index.html 导言 为了在Android中创建动态的多面板用户界面,你需要 ...

  5. Android官方开发文档Training系列课程中文版:高效显示位图之位图缓存

    原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/cache-bitmap.html 往UI界面中加载单张图片的过程是很简单的, ...

  6. Android官方开发文档Training系列课程中文版:打印内容之HTML文档打印

    原文地址:http://android.xsoftlab.net/training/printing/html-docs.html 在Android中打印内容要比打印照片要复杂一些,它要求将文本与图像 ...

  7. Android官方开发文档Training系列课程中文版:布局性能优化之布局层级优化

    原文地址:http://android.xsoftlab.net/training/improving-layouts/index.html 引言 布局是直接影响用户体验的关键部分.如果实现的不好,那 ...

  8. Android官方开发文档Training系列课程中文版:线程执行操作之创建多线程管理器

    原文地址:http://android.xsoftlab.net/training/multiple-threads/create-threadpool.html 上节课我们学习了如何定义一个任务.如 ...

  9. Android官方开发文档Training系列课程中文版:电池续航时间优化之监测电池电量及充电状态

    原文地址:http://android.xsoftlab.net/training/monitoring-device-state/index.html 引言 作为一款优秀的APP应用,应该总是想方设 ...

最新文章

  1. 用 C 语言开发一门编程语言 — 跨平台的可移植性
  2. Spring Cloud Zuul
  3. 预计2021年移动渠道将占中国网络广告支出82%
  4. Bitmap recycle()
  5. PowerDesigner 逆向工程 从SQL文件转换成PDM 从PDM转成CDM
  6. c语言怎么运行出星星,C语言打印星星的问题
  7. Delphi十进制和十六进制互转
  8. gitee添加成员_成员权限管理,到底能有多精细?
  9. Java 总结,会不断更新
  10. [En]Web Forms Syntax Reference
  11. 此计算机上缺少vc2005_sp1_with_atl_fix_redist
  12. 唐太宗管理之道:收人,收心,收天下
  13. STM32F407-用TB6600驱动器驱动57步进电机(代码+连线)
  14. android一些学习网站
  15. kubelet垃圾回收配置
  16. 中华人民共和国刑事诉讼法
  17. 关于倾斜测量中的“无感惯导”
  18. 【Java编程系列】Java自定义标签-Tag
  19. 四大网络功能 HP演绎教育PC智能魔方
  20. go run undefined

热门文章

  1. C++中string.find()的误用
  2. 大佬为何可以把单片机描述得如此形象生动?
  3. 目标检测发展路程(一)——Two stage
  4. 手机java软件_浅谈软件开发就业前景
  5. Node 中的开发环境与生产环境 和 使用Morgan打印请求信息
  6. JavaScript+ Canvas开发趣味小游戏《贪吃蛇》
  7. LeetCode 2116. 判断一个括号字符串是否有效(栈)
  8. LeetCode 296. 最佳的碰头地点(坐标独立+中位数的地方最近)
  9. LeetCode 573. 松鼠模拟(数学)*
  10. LeetCode 840. 矩阵中的幻方(数学)