Bitmap的加载离不开BitmapFactory类,关于Bitmap官方介绍Creates Bitmap objects from various sources, including files, streams, and byte-arrays.查看api,发现和描述的一样,BitmapFactory类提供了五类方法用来加载Bitmap:
/** * Decode a new Bitmap from an InputStream. This InputStream was obtained from * resources, which we pass to be able to scale the bitmap accordingly. */ @Nullable public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value, @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) { validate(opts); // 如果默认传入的opts为null ,则实例化一个默认的opts // 这个decodeFile是不一样的。 if (opts == null) { opts = new Options(); }
if (opts.inDensity == 0 && value != null) { final int density = value.density; if (density == TypedValue.DENSITY_DEFAULT) { opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (density != TypedValue.DENSITY_NONE) { opts.inDensity = density; } } if (opts.inTargetDensity == 0 && res != null) { opts.inTargetDensity = res.getDisplayMetrics().densityDpi; } return decodeStream(is, pad, opts); }
/** * Synonym for opening the given resource and calling * 从资源文件中读取解码成Bitmap。 */ public static Bitmap decodeResource(Resources res, int id, Options opts) { validate(opts); Bitmap bm = null; InputStream is = null; try { final TypedValue value = new TypedValue(); // 获取资源文件的输入流 is = res.openRawResource(id, value); // 我们可以看到这个方法最终是调用decodeResourceStream bm = decodeResourceStream(res, value, is, null, opts); } catch (Exception e) { /* do nothing. If the exception happened on open, bm will be null. If it happened on close, bm is still valid. */ } finally { try { if (is != null) is.close(); } catch (IOException e) { // Ignore } }
if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); }
return bm; }
/** * 从资源文件中读取解码成Bitmap。 */ public static Bitmap decodeResource(Resources res, int id) { return decodeResource(res, id, null); }
/** * */ public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) { if ((offset | length) < 0 || data.length < offset + length) { throw new ArrayIndexOutOfBoundsException(); } validate(opts);
if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); } setDensityFromOptions(bm, opts); } finally { Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); }
return bm; }
/** * */ public static Bitmap decodeByteArray(byte[] data, int offset, int length) { return decodeByteArray(data, offset, length, null); }
/** * Set the newly decoded bitmap's density based on the Options. */ private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) { if (outputBitmap == null || opts == null) return;
final int density = opts.inDensity; if (density != 0) { outputBitmap.setDensity(density); final int targetDensity = opts.inTargetDensity; if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) { return; }
byte[] np = outputBitmap.getNinePatchChunk(); final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); if (opts.inScaled || isNinePatch) { outputBitmap.setDensity(targetDensity); } } else if (opts.inBitmap != null) { // bitmap was reused, ensure density is reset outputBitmap.setDensity(Bitmap.getDefaultDensity()); } }
/** * */ @Nullable public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding, @Nullable Options opts) { // we don't throw in this case, thus allowing the caller to only check // the cache, and not force the image to be decoded. if (is == null) { return null; } validate(opts);
Bitmap bm = null;
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); try { if (is instanceof AssetManager.AssetInputStream) { final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset(); bm = nativeDecodeAsset(asset, outPadding, opts); } else { bm = decodeStreamInternal(is, outPadding, opts); }
if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); }
/** * Private helper function for decoding an InputStream natively. Buffers the input enough to * do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null. */ private static Bitmap decodeStreamInternal(@NonNull InputStream is, @Nullable Rect outPadding, @Nullable Options opts) { // ASSERT(is != null); byte [] tempStorage = null; if (opts != null) tempStorage = opts.inTempStorage; if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; return nativeDecodeStream(is, tempStorage, outPadding, opts); }
/** * Decode an input stream into a bitmap. If the input stream is null, or * cannot be used to decode a bitmap, the function returns null. * The stream's position will be where ever it was after the encoded data * was read. * * @param is The input stream that holds the raw data to be decoded into a * bitmap. * @return The decoded bitmap, or null if the image data could not be decoded. */ public static Bitmap decodeStream(InputStream is) { return decodeStream(is, null, null); }
我们在使用bitmap时,经常会遇到内存溢出等情况,这是因为图片太大或者android系统对单个应用施加的内存限制等原因造成的,比如上述方法1加载一张照片时就会报:06-28 10:43:30.777 26007-26036/com.peak.app W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (3120x4160, max=4096x4096),而方法2加载一个3+G的照片时会报Caused by: java.lang.OutOfMemoryError: Failed to allocate a 144764940 byte allocation with 16765264 free bytes and 109MB until OOM所以,高效的使用bitmap就显得尤为重要,对他效率的优化也是如此。