На смартфонах деяких виробників (HTC і Gibabyte, як мінімум) при використанні методу BitmapFactory.decodeFile() генерується виключення java.lang.OutOfMemoryError виду:
04-10 12:39:17.319: E/dalvikvm-heap(4413): 20155392-byte external allocation too large for this process.
04-10 12:39:17.369: E/GraphicsJNI(4413): VM won't let us allocate 20155392 bytes
04-10 12:39:17.369: D/skia(4413): --- decoder->decode returned false
04-10 12:39:17.369: W/dalvikvm(4413): threadid=1: thread exiting with uncaught exception (group=0x40018560)
04-10 12:39:17.369: E/AndroidRuntime(4413): FATAL EXCEPTION: main
04-10 12:39:17.369: E/AndroidRuntime(4413): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:284)
04-10 12:39:17.369: E/AndroidRuntime(4413): at ...........
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.app.Activity.dispatchActivityResult(Activity.java:3908)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.app.ActivityThread.deliverResults(ActivityThread.java:2528)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.app.ActivityThread.handleSendResult(ActivityThread.java:2574)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.app.ActivityThread.access$2000(ActivityThread.java:117)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:961)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.os.Handler.dispatchMessage(Handler.java:99)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.os.Looper.loop(Looper.java:130)
04-10 12:39:17.369: E/AndroidRuntime(4413): at android.app.ActivityThread.main(ActivityThread.java:3683)
04-10 12:39:17.369: E/AndroidRuntime(4413): at java.lang.reflect.Method.invokeNative(Native Method)
04-10 12:39:17.369: E/AndroidRuntime(4413): at java.lang.reflect.Method.invoke(Method.java:507)
04-10 12:39:17.369: E/AndroidRuntime(4413): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:875)
04-10 12:39:17.369: E/AndroidRuntime(4413): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:633)
04-10 12:39:17.369: E/AndroidRuntime(4413): at dalvik.system.NativeStart.main(Native Method)
Ця помилка, наприклад, може бути викликана наступним кодом при спробі отримати зображення високої роздільної здатності з файлової системи або за допомогою камери смартфона:
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bm = BitmapFactory.decodeFile(path, options);
Вирішується помилка за допомогою додавання методу getScaledBitmap() (знайденого колись на просторах інтернету):
private Bitmap getScaledBitmap(String path) {
Uri uri = Uri.parse(path);
InputStream in = null;
try {
final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
in = new FileInputStream( uri.getPath() );
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, o);
in.close();
int scale = 1;
while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) {
scale++;
}
Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth
+ ", orig-height: " + o.outHeight);
Bitmap b = null;
in = new FileInputStream( uri.getPath() );
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
o = new BitmapFactory.Options();
o.inSampleSize = scale;
b = BitmapFactory.decodeStream(in, null, o);
// resize to desired dimensions
int height = b.getHeight();
int width = b.getWidth();
Log.d(TAG, "1th scale operation dimenions - width: " + width
+ ", height: " + height);
double y = Math.sqrt(IMAGE_MAX_SIZE
/ (((double) width) / height));
double x = (y / height) * width;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(b, (int) x,
(int) y, true);
b.recycle();
b = scaledBitmap;
System.gc();
} else {
b = BitmapFactory.decodeStream(in);
}
in.close();
Log.d(TAG, "bitmap size - width: " + b.getWidth() + ", height: "
+ b.getHeight());
return b;
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
return null;
}
}
Вищенаведений код зменшує зображення до розмірів 1.2 мегапікселя і потім створює об'єкт Bitmap зі зменшеного зображення, замість створення повнорозмірного, що часто не є гострою необхідністю.
Також необхідно привести код, який викликав помилку, до вигляду:
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bm = null;
try{
bm = BitmapFactory.decodeFile(path, options);
} catch( OutOfMemoryError e ){
bm = getScaledBitmap(path);
}