您的位置:首页 > 其它

使用bitmap

2016-04-02 10:29 267 查看
本文参考Google文档。

你要学习如何使用bitmap加载,否则的话你会快速消耗内存和看到以下异常:

java.lang.OutofMemoryError: bitmap size exceeds VM budget
在你的应用里造成这样的情况,原因应该是以下这些:

1、手机设备在使用系统资源是有约束的。安卓的单个应用缓存是16兆,不同的系统版本有不同的分配,但是请你记住,应用必须要优化执行,不可以超出内存限制。

2、bitmap会占用大量的内存,特别是丰富的图像,比如照片。例如,照相机在(2592 x 1936)手机上拍照,bitmap的配置,在2.3系统默认使用ARGB_8888显示,加载在内存中,大约要使用19兆(2596 x 1936的图片),在一些设备上会用尽资源。

3、安卓UI频繁请求几个bitmap加载。比如像listview、gridview、viewpage这些控件通常包括了很多bitmap,手机一滑动,会加载很多。

下面是你应该要知道的知识。

1、如何有效加载大的bitmap。会告诉你怎么去解析大的bitmap而不超出限制。

2、在UI线程中处理bitmap。bitmap的图像解析(调整大小、下载图像等)这些操作不应该发生在主线程。会告诉你如何在后台线程中去处理bitmap相关操作并使用AsyncTask处理一些并发问题。

3、缓存bitmap。当UI有很多bitmap的时候,告诉你如何去在内存和硬盘中使用bitmap,从而加快流畅性和响应能力在界面上。



4、管理bitmap。会告诉你怎样管理bitmap在应用中发挥最大性能。

5、加载bitmap在UI上。告诉你如何应用bitmap展示在控件上,比如viewpage和gridview使用后台线程和bitmap缓存。

如何有效加载大的bitmap。

图片有各种形状和大小。在许多情况下,你需要在应用中去加载一张大图。例如,图库在各种不同的分辨率手机上去加载高清图片。鉴于图片有内存限制,在这种情况下你只能加载比较低的版本。较低分辨率的版本应该与显示它的用户界面组件的大小匹配。更高分辨率的图像不提供任何可见的好处,但仍然占用了宝贵的内存,并导致额外的性能开销。应该通过解码大图片不超过每个应用程序的内存限制在内存较小的采样版本加载。

首先要读取bitmap的尺寸和类型。BitmapFactory提供了几个方法去建立一个bitmap,例如(decodeByteArray(), decodeFile(), decodeResource()等方法)选择一个合适的方法基于你的图片。这些方法试图分配内存而且很容易导致内存溢出就是开篇提到的异常。每一种方法都有一个指定的签名,在你的
BitmapFactory.Options
这个类里。你要设置
inJustDecodeBounds
这个属性为true,它就会在解析的时候不会分配内存。然后这个bitmap会返回null,但是会返回一些参数,比如(
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;
为了避免内存溢出这个异常,你必须在建立bitmap之前检查它的尺寸和类型信息,除非你特别相信这张图片的来源。它可以为你提供可预见的图像大小数据,更好的使用内存。

然后在内存加载一个缩小版的。现在我们已经知道了图片的尺寸信息,我们可以决定是加载一个样板还是完全加载图片,这里有一些因素要考虑:

1、预估完全加载图片的内存。

2、你应用的内存大小,不仅要加载图片因为内存里还有其他东西,内存是否够用。

3、当前设备的尺寸大小和分辨率。

例如,你在一个128 x 96像素的imageview上加载缩略图,你不应该在内存里加载1024 x 768的图片,这是不值得的。告诉解码器样本图像,你应该在内存中加载一个小的版本,给你的
BitmapFactory.Options
对象的
inSampleSize
属性进行设置。例如,你要加载一张分辨率为2048 x 1536的图片,那你
要把
inSampleSize
设置为4,加载尺寸为512
x 384.这样的话在内存中只需要0.75兆(假设bitmap配置为[code]ARGB_8888
),如果全图加载需要12兆
(假设bitmap配置为ARGB_8888
)[/code]。下面的方法可以根据传入的宽和高,计算出合适的inSampleSize值:

public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth,
int reqHeight) {

  // 源图片的高度和宽度
   
  finalint height = options.outHeight;
 
  finalint width = options.outWidth;

  int inSampleSize = 1;
 
  if (height > reqHeight width > reqWidth) {
 
       // 计算出实际宽高和目标宽高的比率
    
       finalint heightRatio = Math.round((float) height / (float) reqHeight);
  
       finalint widthRatio = Math.round((float) width / (float) reqWidth);
     
       // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
 
       // 一定都会大于等于目标的宽和高。
  
       inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
     
}
 
    return inSampleSize;

}


使用这个方法,首先你要将BitmapFactory.Options的inJustDecodeBounds属性设置为true,解析一次图片。然后将BitmapFactory.Options连同期望的宽度和高度一起传递到到calculateInSampleSize方法中,就可以得到合适的inSampleSize值了。之后再解析一次图片,使用新获取到的inSampleSize值,并把inJustDecodeBounds设置为false,就可以得到压缩后的图片了。
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth,
int reqHeight) {

    // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
    
    final BitmapFactory.Options options = new BitmapFactory.Options();
 
    options.inJustDecodeBounds = true;
     
BitmapFactory.decodeResource(res, resId, options);
  
    // 调用上面定义的方法计算inSampleSize值
    
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // 使用获取到的inSampleSize值再次解析图片
     
options.inJustDecodeBounds = false;
  

    return BitmapFactory.decodeResource(res, resId, options);

}
下面的代码非常简单地将任意一张图片压缩成100*100的缩略图,并在ImageView上展示。
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(),
R.id.myimage, 100, 100));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: