Android Bitmap 知识点整理

@bingoogolapple 2016-07-04 10:39:56发表于 bingoogolapple/bingoogolapple.github.io Android

Android中图片占用内存的计算

Android中一张图片(Bitmap)占用的内存=图片长度 x 图片宽度 x 单位像素占用的字节数
注:图片长度和图片宽度的单位是像素。

创建一个Bitmap时,其单位像素占用的字节数由其参数BitmapFactory.Options的inPreferredConfig变量决定。inPreferredConfig为Bitmap.Config类型,它可以为以下值

图片格式(Bitmap.Config) 占用内存的计算方式 一张100*100的图片占用内存的大小
ALPHA_8代表8位Alpha位图 图片长度*图片宽度 100 * 100=10000字节
ARGB_4444代表16位ARGB位图 图片长度*图片宽度 * 2 100 * 100 * 2=20000字节
ARGB_8888代表32位ARGB位图 图片长度*图片宽度 * 4 100 * 100 * 4=40000字节
RGB_565代表8位RGB位图 图片长度*图片宽度 * 2 100 * 100 * 2=20000字节

另外,需要注意这里的图片占用内存是指在Navtive中占用的内存,当然Bitmap使用的绝大多数内存就是该内存。
因为我们可以简单的认为它就是Bitmap所占用的内存。
Bitmap对象在不使用时,我们应该先调用recycle(),然后才它设置为null。
虽然Bitmap在被回收时可以通过BitmapFinalizer来回收内存。但是调用recycle()是一个良好的习惯。
Android 2.3 之前 Bitmap 的引用是放在堆中的,而 Bitmap 的数据部分是放在栈中的,需要用户调用recycle 方法手动进行内存回收,而在 Android 2.3 之后,整个 Bitmap 包括数据和引用都放在了堆中

  • 无论图片质量好坏,加载到内存中占用内存的大小只与图片大小、Config图片质量参数配置有关,压缩图片只是让打包的apk减小,而运行时的内存大小是无关的。
  • 调用recycle之后,虽然不立即释放,但当内存不足时还是会去释放的,并分配给新对象
  • 图片显示到imageView 和 解码到bitmap,占用内存一样多;
    activity finish后,可能不会立即释放内存,但内存不足时应该是会释放掉的,只要这个activity里的对象没被其他类引用;所以android里不用过于关注activity里各种内存占用、释放,只要引用不被其他类引用
  • xml里设置ImageView的src 和 代码中设置imageResource占用的内存一样多。那么如果imageView的width 不是wrap_content呢?比如 width=100,height=150,经测试,占用的内存情况与上述wrap_content完全相同; 看来android底层加载图片时,也没有进行内存优化,未根据imageView控件大小对图片做缩放后再加载进来;或者什么原因不优化
  • 根据图片控件的宽高来计算BitmapFactory.Options的inSampleSize属性的值来优化Bitmap加载内存
  • 创建一个空的bitmap 480 * 800, 占用的内存大小是不是与 加载一张480 * 800的图片大小一样。bitmap占用内存大小 与三个因素有关: width 、height、config质量参数
  • android bitmap占用的内存代码直接计算bitmap.getRowBytes() * bitmap.getHeight()
  • PC上看 480 * 800的图片,读入内存bitmap后大小 getWidth getHeight值变了,getWidth: 640, getHeight: 1067。drawable存放目录导致,我放在drawable-hdpi目录下,机型是 720 * 1080分辨率的(320密度),改放在 drawable-xhdpi目录下,bitmap.getWidth() 值就与预期一致了为480
  • drawable目录,android图片处理有自动缩放功能,如果xhdpi的机型,读取hdpi目录,则会认为图适配的机型是hdpi,用在 xhdpi的机型需要等比例放大

打印当前程序占用的内存

public static void printMem(String when) {  
    //程序可用的最大内存  
    float maxMem = Runtime.getRuntime().maxMemory() / 1024 / 1024;  
    //程序当前占用的内存  
    float totalMem = Runtime.getRuntime().totalMemory() / 1024 / 1024;  
    //freeMem != maxMem - totalMem  
    //我理解 freeMem应该是 当前分配给该程序的内存 - totalMem, 当前分配给程序的内存时动态的(在小于maxMem范围内)  
    //同virtualbox安装的ubuntu虚拟机占用内存类似,设置个最大内存,但实际占用内存时动态分配的  
    float freeMem = Runtime.getRuntime().freeMemory() / 1024 / 1024;  

    bm(when + ": maxMem | totalMem | freeMem : " + maxMem + "M|" + totalMem + "M|" + freeMem + "M");  
}