高效加载较大的 Bitmaps
图片有各种形状和各种大小,在很多情况下,图片的实际大小都比图片在应用中所显示的大小要大的多,比如Android系统自带的
Gallery
应用显示的照片实际的分辨率通常比手机设备的密度要高很多
Gallery
应用显示的照片实际的分辨率通常比手机设备的密度要高很多
考虑到我们是在开发一款内存使用受限的应用,理想的情况下,我们只是想把一个低分辨率版本的位图载入内存,一般来说这个低分辨率版本的位图要跟UI元件实际需要显示的大小相符。一张高分辨率的图片并不会给我们带来任何明显的好处,但却会占用宝贵的内存资源和产生额外的性能开销
获取Bitmap的尺寸大小和类型
BitmapFactory 类为我们提供了几种decoding方法(
decodeByteArray()
,
decodeFile()
,
decodeResource()
, etc)来从不同的来源创建出
Bitmap ,如何选择最恰当的decode方法取决于你的图片数据来源,这些方法都会去尝试申请内存来构建Bitmap对象,所有很容易就会导致一个
OutOfMemory 异常,每种类型的decode方法都有额外的签名来让你通过
BitmapFactory.Options
类来指定decoding选项,当我们decoding的时候把inJustDecodeBounds 属性设置为
true 可以避免申请内存,虽然会返回一个
null Bitmap对象 ,但是会为我们传入的
BitmapFactory.Options
对象设置outWidth
,
outHeight
and
outMimeType
等属性的值,这个技术可以让你在构建Bitmap对象之前事先知道它的大小和类型
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;
为了避免
异常,
在decoding Bitmap之前你有必要去检测Bitmap的大小和类型,除非你真的是非常清楚你要decoding的Bitmap的大小,还有这个大小要适合当前应用内存环境
java.lang.OutOfMemory
异常,
在decoding Bitmap之前你有必要去检测Bitmap的大小和类型,除非你真的是非常清楚你要decoding的Bitmap的大小,还有这个大小要适合当前应用内存环境
载入‘缩小版’的Bitmap到内存
现在我们已经知道了Bitmap的大小,这将有助于我们来决策是载入整张Bitmap还是载入'缩小版'的Bitmap,这里有一些因素需要进行考虑
一、
载入整张图片预计要使用多少内存
载入整张图片预计要使用多少内存
二、
在考虑到其它方面内存需要的情况下,你想把多少数量的内存给Bitmap使用
在考虑到其它方面内存需要的情况下,你想把多少数量的内存给Bitmap使用
四、
当前设备屏幕的大小和密度
当前设备屏幕的大小和密度
我们应该告诉decoder,图像需要进行抽样,载入一个更小号的Bitmap到内存中,设置
true 。例如,一张分辨率为
2048x1536 像素的图片,如果decode的时候把
512x384 ,载入内存耗费0.75M而不是载入整张时的12M (假设位图的配置为
inSampleSize 的方法
BitmapFactory.Options
对象的inSampleSize
属性为true 。例如,一张分辨率为
2048x1536 像素的图片,如果decode的时候把
inSampleSize
设置为4,那么得到的最终图片的大小大约为512x384 ,载入内存耗费0.75M而不是载入整张时的12M (假设位图的配置为
ARGB_8888
) ,下面有一个在目标高和宽基础上计算inSampleSize 的方法
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) { if (width > height) { inSampleSize = Math.round((float)height / (float)reqHeight); } else { inSampleSize = Math.round((float)width / (float)reqWidth); } } return inSampleSize; }
NOTE :
inSampleSize
值是2的幂的话,对于decoder来说会更快和更高效。然而,如果你想把调整过大小的位图缓存到内存或硬盘上时,依然非常有意义decoding最合适的位图大小,这样有助于节省内存或节省硬盘空间
下面是一个获取位图的方法
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
这个方法可以很容易的做到在任意显示尺寸大小的UI元件中去载入一张位图
mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));