На смартфонах некоторых производителей (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);
}