Android Bitmap
高效的加载大的Bitmap
从各种图片源获取的Bitmap往往都是比较大的,如果直接加载到内存中容易造成OOM异常,现提供如下思路:
先获取Bitmap的大小和类型
示例代码:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResource(), R.id.image, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
将inJustDecodeBounds设置为true,从而避免分配内存,因为在这里我们只需要Bitmap相关的一些参数。
将缩小的图片加载到内存中
通过上面的计算,我们已经知道bitmap的相关信息,这些足以帮助我们来判断是直接加载全尺寸大图,还是一个压缩图了。一般我们基于如下的考虑因素:
- 评估全图的内存使用大小
- 应用的其他部分对内存的需求量
- ImageView的尺寸或者将要加载图片的尺寸
- 当前设备的屏幕大小和分辨率
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
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;
// 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;
}
在非UI线程中处理Bitmap
BitmapFatory.decode方法,如果源数据来自磁盘或者网络地址,那么不能放在主线程中。
缓存Bitmap
Memory Cache
LruCache 使用LinkedHashMap结构
磁盘缓存
ContentProvider或许是一个很合适的选择,用来存储缓存的图片。 DiskLruCache
管理Bitmap内存
推荐的策略是根据不同的系统版本采用不同的方法。
-
在Android2.2(API 8)或者更低的版本,当发生垃圾收集时,app的进程会被停止。这个会导致延迟,是个性能上的问题。Android2.3增加了同步GC,这意味着,如果bitmap不再引用时,内存会被立马回收。
-
在Android2.3.3(API 10)或者以下,bitmap背后的像素数据是保存在native内存中的。和bitmap本身是分开的,而bitmap是保存在Dalvik heap上的。在本地内存中的像素数据不会像预想的那样释放,将潜在的导致内存溢出,并导致应用奔溃。在Android3.0(API 11)中,像素数据和bitmap一起存放在了Dalvik的堆上。
下面的例子将介绍在不同的Android版本上如何优化bitmap的内存管理
Android 2.3.3 及以下
在这种情况下,建议使用Bitmap的recycle方法。如果你显示大量的bitmap数据,那么很有可能导致OOM错误。recycle方法让app尽可能快的回收内存。
Anroid 3.0 或者更高
Android3.0(API 11)引入了BitmapFactory.Options.inBitmap。如果这个值被设置了,那么bitmap的内存将被复用,并且提升了性能,因为不再分配和释放内存了。然后它也有确定的限制,在Android 4.4(API 19) 中,只有相同大小或者小于它的bitmap才被支持。