Android Bitmap内存优化

番外篇

首先我们先了解位图和矢量图的区别

  • 位图
    位图使用我们称为像素的一格一格的小点来描述图像.计算机屏幕其实就是一张包含大量像素点的网格.在位图中,平时看到的图像将会由每一个网格中的像素点的位置和色彩值来决定.每一点的色彩是固定的,当我们在更高分辨率下观看图像时,每一个小点看上去就像是一个个马赛克色块.当图像放大后,边缘变得模糊不清.位图图像的清晰度取决于图像大小与分辨率
  • 矢量图
    矢量图使用线段和曲线描述图像,所以称为矢量,同时图形也包含了色彩和位置信息.当您进行矢量图形的编辑时,您定义的是描述图形形状的线和曲线的属性,这些属性将被记录下来.对矢量图形的操作,例如移动,重新定义尺寸,重新定义形状,或者改变矢量图形的色彩,都不会改变矢量图形的显示品质.您也可以通过矢量对象的交叠,使得图形的某一部分被隐藏,或者改变对象的透明度.矢量图形是”分辨率独立”的,这就是说,当您显示或输出图像时,图像的品质不受设备的分辨率的影响.

BitMap和内存的关系

说到内存就会想到内存溢出。内存溢出就是分配的内存不足以放下数据项序列。如在一个域中输入的数据超过了它的要求就会系统能给你的,系统不能满足需求,于是产生溢出。或者可以这样理解堆上分配的内存没有被释放,从而失去对其控制。这样会造成程序能使用的内存越来越少,导致系统运行速度减慢,严重情况会使程序宕掉。

在日常开发中影响位图的占用大小的几个关键点:

1
2
3
1.图片长度
2.图片宽度
3.每个像素所占的字节(Bitmap的RGB值)

BitMap的的计算公式为:
一张图片(BitMap)占用的内存=图片长度图片宽度单位像素占用的字节数
详解介绍如何计算Bitmap内存占用
注意:

Bitmap的占用空间大小和目标文件的大小无关,主要是图片的长和宽。

Bitmap内存优化

上面说了这么多基本的概念和原理。下面就来看看实战中应该如果避免内存溢出。

1.及时释放内存

1.为什么要及时回收Bitmap的内存

Android系统有自己的垃圾回收机制,可以不定期的回收掉不使用的内存空间,当然也包括Bitmap的空间。那为什么还需要这个方法呢?Bitmap类的构造方法都是私有的,,只能通过BitmapFactory类的来实例化一个Bitmap。仔细查BitmapFactory的源代码可以看到,生成Bitmap对象最终都是通过JNI调用方式实现的。所以,加载Bitmap到内存里以后,是包含两部分内存区域的。简单的说,一部分是Java部分的,一部分是C部分的。这个Bitmap对象是由Java部分分配的,不用的时候系统就会自动回收了,但是那个对应的C可用的内存区域,虚拟机是不能直接回收的,这个只能调用底层的功能释放。所以需要调用recycle()方法来释放C部分的内存。从Bitmap类的源代码也可以看到,recycle()方法里也的确是调用了JNI方法了。
那如果不调用recycle(),是否就一定存在内存泄露呢?也不是的。Android的每个应用都运行在独立的进程里,有着独立的内存,如果整个进程被应用本身或者系统杀死了,内存也就都被释放掉了,当然也包括C部分的内存。

2.手动释放内存

下面是释放Bitmap的示例代码片段。

1
2
3
4
5
6
7
8
9
//在Activity或Fragment的onDestory方法中进行回收(必须确保bitmap不在使用)
public void onDestory(){
// 先判断是否已经回收
if(bitmap != null && !bitmap.isRecycled()){
// 回收并且置为null
bitmap.recycle();
bitmap = null;
}
}

2.进行图片压缩

如果我在开始的时候加载一张很大的图,直接超过了分配的内存大小,那也会引起内存溢出。这个时候我们就需要对加载的BitMap的大小进行控制,这个时候就需要用到图片压缩。
图片压缩比较靠谱的几种办法

3.捕获异常

在Android系统里,读取位图Bitmap时,分给虚拟机中的图片的堆栈大只有8M,为了避免应用在分配Bitmap内存的时候出现OutOfMemory异常以后Crash掉,所以在实例化Bitmap的代码中,一定要对OutOfMemory异常进行捕获。

以下是代码示例。

1
2
3
4
5
6
7
Bitmap bitmap = null;  
try {
// 实例化Bitmap
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
//
}

这里对初始化Bitmap对象过程中可能发生的OutOfMemory异常进行了捕获。如果发生了OutOfMemory异常,应用不会崩溃,而是得到了一个默认的Bitmap图。

经验分享:

很多开发者会习惯性的在代码中直接捕获Exception。但是对于OutOfMemoryError来说,这样做是捕获不到的。因为OutOfMemoryError是一种Error,而不是Exception。在此仅仅做一下提醒,避免写错代码而捕获不到OutOfMemoryError。

分享到 评论