热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LoadingLargeBitmapsEfficiently(高效的加载大的位图)

学习如何使用常见的技术来处理和加载Bitmap对象,并且保持用户界面(UI)组件响应以及避免超过应用程序内存限制。(ExceedingApplicationMemoryLimit)如果不

学习如何使用常见的技术来处理和加载 Bitmap 对象,并且保持用户界面(UI)组件响应以及避免超过应用程序内存限制。(Exceeding Application Memory Limit)

如果不小心,位图(Bitmpa)会快速的消耗应用的内存预算,从而产生常见的OME异常,并且导致应用程序崩溃:java.lang.OutofMemoryError: bitmap size exceeds VM budget.

在Android Application加载位图(Bitmap)困难的原因:

  1.移动设备通常会有约束的系统资源。Android对于每一个应用程序仅有16MB的内存可用。Android 兼容定义文档(Android Compatibility Definition Document (CDD)),虚拟机兼容性(Virtual Machine Compatibility)为各种屏幕尺寸和密度提供了所需的最小应用程序存储器。应用程序应该对程序进行优化,以执行此最小内存限制。但是通常情况下,不同的设备都配置了更高的限制。

  2.位图(Bitmap)占用大量的内存,尤其对于像照片一样的大图像。例如,在Galaxy Nexus的摄像头拍到2592x1936像素(5像素)。如果用位图配置argb_8888(默认从Android 2.3开始)然后加载这个图像到内存大约需要19mb内存(2592×1936×4字节),马上就耗尽了一些设备上对于应用程序的限制。

  3.Android App的UI通常需要一次性加载几个位图。例如组件ListView,GridView和ViewPager通常在屏幕上包括多个图片,例如,手指轻轻一点在屏幕上显示不同的位图。

(一)Loading Large Bitmaps Efficiently(高效的加载大位图)

图像通常有各种各样的形状和大小。通常情况下,图像比应用程序UI所需要的大的多。例如,系统的Gallery应用程序使用Android设备的相机来显示图片,通常是比设备的屏幕分辨率更高的照片。

假设你在有限的内存中工作,理想的情况下,你只想在内存中加载一个低分辨率版本的图片。较低分辨率的图片应该匹配显示照片UI组件的大小。很显然,这时候,由于额外的缩放,一个更高分辨率的图片不仅不能带来更好的视觉效果,而且会占用宝贵的内存,并且带来额外的性能开销。

通过在内存中加载一个较小的采样版本的照片,将有助于帮助我们在不超过每个应用程序内存限制的情况下解码一个大的位图。

(二)Read Bitmap Dimensions and Types(读取位图的大小和类型)

BitmapFactory 类提供了几个解码的方法来通过各种不同的资源来创建位图,例如decodeByteArray(),decodeFile(),decodeResource()等方法。根据图像数据源,选择最合适的解码方法。这些方法会根据这些位图的实际大小来申请内存,因此,这些方法非常容易导致OutOfMemory异常。每种解码方法类型都有一个附加的参数,这个参数允许你通过BitmapFactory.Options类来声明解码选项。设置inJustDecodeBounds属性为true,在解码的过程中能够避免申请内存。这时候,解码方法会返回null的位图对象,然后可以获取outWidth, outHeight, outMimeType。这个技术允许你在位图的结构(和内存分配)之前读区图像数据的尺寸和类型。

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

为了避免java.lang.OutOfMemeory异常,在解码之前,检查位图的尺寸并且做响应的处理。除非你确信你提供的资源即图像数据的大小兼容你的可用内存大小。

(三)Load a Scaled Down Version into Memory(加载一个按比例缩小的版本到内存中)

由第二节,可以确认图像的尺寸,用它可以来决定是否应该加载完整的图像还是采样本图像到内存中。考虑的因素如下:

  1.估计加载整个图像到内存中的所需内存。

  2.除了应用程序需要的其他内存外,你计划分配给该图像的内存数量。

  3.图像被加载到ImageView或者其他UI组件的目标尺寸。

  4.当前设备的屏幕尺寸和像素。

例如,图像将会被最终加载到一个128*96d的ImageView,这时,如果将一个图像为1024*768像素的图像整个加载到内存的话是不划算的。

为了告诉decoder加载一个图像的缩略图,也就是说加载一个底分辨率的图像到内存中,需要设置 BitmapFactory.Options的inSampleSize属性值为true。

关于ARGB_8888 ALPHA_8 ARGB_4444 RGB_565理解

A: 透明度

R: 红色

G: 绿

B: 蓝

Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 

Bitmap.Config ARGB_8888:每个像素占四位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位

Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。

一般情况下我们都是使用的ARGB_8888,由此可知它是最占内存的,因为一个像素占32位,8位=1字节,所以一个像素占4字节的内存。假设有一张480x800的图片,如果格式为ARGB_8888,那么将会占用1500KB的内存。

例如,一个图像分辨率为2048*1536,如果设置inSampleSize的值为4,则会产生一个接近于512*384的位图,加载到内存中之需要0.75MB而不是12MB(整个图像大小,假设位图配置为ARGB_8888 )

原位图占的总位数: 2048*1536*32(2048*1536*32/8/1024/1024 MB)

Here’s a method to calculate a sample size value that is a power of two based on a target width and height:

这里有一个计算samplesize值的方式:基于目标宽和高的2的乘方。

计算inSample的最大取值,且inSample的取值是2的乘方。并且同时保持高度和宽度大于要求的宽和高。

 

 1 public static int calculateInSampleSize(
 2             BitmapFactory.Options options, int reqWidth, int reqHeight) {
 3     // Raw height and width of image
 4     final int height = options.outHeight;
 5     final int width = options.outWidth;
 6     int inSampleSize = 1;
 7 
 8     if (height > reqHeight || width > reqWidth) {
 9 
10         final int halfHeight = height / 2;
11         final int halfWidth = width / 2;
12 
13         // Calculate the largest inSampleSize value that is a power of 2 and keeps both
14         // height and width larger than the requested height and width.
15         while ((halfHeight / inSampleSize) >= reqHeight
16                 && (halfWidth / inSampleSize) >= reqWidth) {
17             inSampleSize *= 2;
18         }
19     }
20 
21     return inSampleSize;
22 }

 

为了使用这个方法,首先需要在inJustDecodeBounds为true的条件下进行decode,然后获取到 options,然后使用新的inSampleSize值,并且设置inJustDecodeBounds为false对图像进行解码。

 1 public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
 2         int reqWidth, int reqHeight) {
 3 
 4     // First decode with inJustDecodeBounds=true to check dimensions
 5     final BitmapFactory.Options optiOns= new BitmapFactory.Options();
 6     options.inJustDecodeBounds = true;
 7     BitmapFactory.decodeResource(res, resId, options);
 8 
 9     // Calculate inSampleSize
10     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
11 
12     // Decode bitmap with inSampleSize set
13     options.inJustDecodeBounds = false;
14     return BitmapFactory.decodeResource(res, resId, options);
15 }

使用这个方法,可以方便的加载人意大的位图到一个尺寸为100*100的ImageView缩略图中。

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

 


推荐阅读
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
author-avatar
钟罄石
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有