Android. Ошибка на смартфонах HTC и Gigabyte: n-byte external allocation too large for this process

апреля
10
2012
Метки: android java

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

Напишите первое сообщение!

Вы должны войти под своим аккаунтом чтобы оставлять комментарии