BitmapFactory.options
BitmapFactory.Options类是BitmapFactory对图片进行解码时使用的一个配置参数类,其中定义了一系列的public成员变量,每个成员变量代表一个配置参数。
图片解码建议配置(inPreferredConfig)
参数inpreferredconfig表示图片解码时使用的颜色模式,也就是图片中每个像素颜色的表示方式
图片颜色:
- 计算机表示一个颜色都需要将颜色对应到一个颜色空间中的某个颜色值,常见的颜色空间为RGB,CMYK等.
- JPEG格式支持RGB,CMYK,而PNG支持RGB,此外绝大数显示器只支持RGB颜色的输入,计算机显示一张图片时,如果图片本身不是RGB颜色空间编码,需将其转化为RGB颜色空间的颜色后在显示,所以非RGB显示会有失真.
颜色透明度:
- 图片包含颜色信息和透明信息,计算机中用一个单独的透明通道表示(Alpha通道),JPEG格式图片不支持透明度,PNG/GIT格式支持透明度.
Android颜色和透明度表示
- Android通常用32位二进制表示一个像素颜色和透明度,即A,R,G,B四个通道,每个通道范围为[0,0xFF]
inperferredConfig参数
- BitmapFactory.Options类是BitmapFractory对图片进行解码时使用的配置参数类, 其中定义一系列public的成员变量(配置参数),inperferredConfig表示图片解码时使用的颜色模式:
- inpreferredConfig参数有四个值:
- ALPHA_8: 每个像素用占8位,存储的是图片的透明值,占1个字节
- RGB_565:每个像素用占16位,分别为5-R,6-G,5-B通道,占2个字节
- ARGB-4444:每个像素占16位,即每个通道用4位表示,占2个字节
- ARGB_8888:每个像素占32位,每个通道用8位表示,占4个字节
图片解码时,默认使用ARGB_8888模式:
Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
ARGB_4444已deprecated,在KITKAT(Android4.4)以上,会用ARGB_8888代替ARGB_4444
使用inperferredConfig注意:
- 如果inPreferredConfig不为null,解码器会尝试使用此参数指定的颜色模式来对图片进行解码,如果inPreferredConfig为null或者在解码时无法满足此参数指定的颜色模式,解码器会自动根据原始图片的特征以及当前设备的屏幕位深,选取合适的颜色模式来解码,例如,如果图片中包含透明度,那么对该图片解码时使用的配置就需要支持透明度,默认会使用ARGB_8888来解码。
- 根据inperferredConfig的解析,就会发现如下bm1,bm2,bm3结果会一样:
java
InputStream stream = getAssets().open(file);
Options op1 = new Options();
op1.inPreferredConfig = Config.ALPHA_8;
Bitmap bm1 = BitmapFactory.decodeStream(stream, null, op1);
Options op2 = new Options();
op2.inPreferredConfig = Config.RGB_565;
Bitmap bm2 = BitmapFactory.decodeStream(stream, null, op2);
Options op3 = new Options();
op3.inPreferredConfig = Config.ARGB_8888;
Bitmap bm3 = BitmapFactory.decodeStream(stream, null, op3);
疑点: 1. 当出现不满足情况时,使用的合适配置是如何选取的?
-
- 如果inPreferredConfig为null,解码时使用的颜色模式会根据图片源文件的类型进行选取,如果图片文件的颜色模式为CMYK,或RGB565,则选取RGB_565。如果是其他类型,则选取ARGB_8888。
-
- 如果inPreferredConfig指定的选项在解码时无法满足,并不会再根据图片文件的类型来选取合适的选项,而是直接使用ARGB_8888选项来解码。例如,图片源文件为RGB566编码的BMP图片,使用ALPHA_8选项来解码时属于不满足的情况,这时会选取ARGB_8888选项来解码,而不是选取RGB565。和inPreferredConfig为null时选取的“合适的”选项并不相同。
-
疑点: 2. 什么情况下使用什么样的配置会出现不满足的情况?
- 所有情况下ARGB_8888配置都可以满足
- 所有情况下ALPHA_8配置都不满足
- 绝大多数情况下RGB565选项都不满足
优化Bitmap的内存使用(inBitmap)
在Android 2.2 (API level 8)以及之前,当垃圾回收发生时,应用的线程是会被暂停的,这会导致一个延迟滞后,并降低系统效率。 从Android 2.3开始,添加了并发垃圾回收的机制, 这意味着在一个Bitmap不再被引用之后,它所占用的内存会被立即回收。
在Android 2.3.3 (API level 10)以及之前, 一个Bitmap的像素级数据(pixel data)是存放在Native内存空间中的。 这些数据与Bitmap本身所占内存是隔离的,Bitmap本身被存放在Dalvik堆中。我们无法预测在Native内存中的像素级数据何时会被释放,这意味着程序容易超过它的内存限制并且崩溃。 自Android 3.0 (API Level 11)开始, 像素级数据则是与Bitmap本身一起存放在Dalvik堆中。
在Android 2.3.3 (API level 10) 以及更低版本上,推荐使用recycle()方法。 如果在应用中显示了大量的Bitmap数据,我们很可能会遇到OutOfMemoryError的错误。 recycle()方法可以使得程序更快的释放内存。
Caution:只有当我们确定这个Bitmap不再需要用到的时候才应该使用recycle()。在执行recycle()方法之后,如果尝试绘制这个Bitmap, 我们将得到”Canvas: trying to use a recycled bitmap”的错误提示。
从Android 3.0 (API Level 11)开始,引进了BitmapFactory.Options.inBitmap字段。如果这个值被设置了,decode方法会在加载内容的时候去reuse已经存在的bitmap. 这意味着bitmap的内存是被reused的,这样可以提升性能, 并且减少了内存的allocation与de-allocation.
- reused的bitmap必须和原数据内容大小一致, 并且是JPEG 或者 PNG 的格式 (或者是某个resource 与 stream).
- reused的bitmap的configuration值如果有设置,则会覆盖掉inPreferredConfig值.
- 你应该总是使用decode方法返回的bitmap, 因为你不可以假设reusing的bitmap是可用的(例如,大小不对).
private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) { // inBitmap only works with mutable bitmaps, so force the decoder to // return mutable bitmaps. options.inMutable = true; if (cache != null) { // Try to find a bitmap to use for inBitmap. Bitmap inBitmap = cache.getBitmapFromReusableSet(options); if (inBitmap != null) { // If a suitable bitmap has been found, // set it as the value of inBitmap. options.inBitmap = inBitmap; } } } static boolean canUseForInBitmap( Bitmap candidate, BitmapFactory.Options targetOptions) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // From Android 4.4 (KitKat) onward we can re-use // if the byte size of the new bitmap is smaller than // the reusable bitmap candidate // allocation byte count. int width = targetOptions.outWidth / targetOptions.inSampleSize; int height = targetOptions.outHeight / targetOptions.inSampleSize; int byteCount = width * height * getBytesPerPixel(candidate.getConfig());