Android Bitmap OutOfMemoryError [duplicate] - android

This question already has answers here:
Android: BitmapFactory.decodeStream() out of memory with a 400KB file with 2MB free heap
(8 answers)
Closed 9 years ago.
I'm having an OutOfMemoryError in my VSD220 (It's a 22" Android based All in one)
for (ImageView img : listImages) {
System.gc();
Bitmap myBitmap = BitmapFactory.decodeFile(path);
img.setImageBitmap(myBitmap);
img.setOnClickListener(this);
}
I really don't know what to do, because this image is below the maximum resolution. The image size is something about (1000x1000), and the display it's 1920x1080.
Any help?
(That foreach cycle is for about 20 elements, it gots broken after 6, or 7 loops..)
Thanks a lot.
Ezequiel.

You should take a look at the training docs for Managing Bitmap Memory. Depending on your OS version, you could use different techniques to allow you to manage more Bitmaps, but you'll probably have to change your code anyway.
In particular, you're probably going to have to use an amended version of the code in "Load a Scaled Down Version into Memory", but I at least have found this section to be particularly useful:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
This method makes it easy to load a bitmap of arbitrarily large size
into an ImageView that displays a 100x100 pixel thumbnail, as shown in
the following example code:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

Are you really sure you want to load the same Bitmap 20 times? Don't you want to load it once and set it inside the loop.
Still, loading a 1000x1000 pixel image is not guaranteed to work, regardless of screen resolution. Remember that a 1000x1000 pixel image takes up 1000x1000x4 bytes =~4MB (if you load it as ARGB_8888). If your heap memory is fragmented/too small you may not have enough space to load the bitmap. You may want to look into the BitmapFactory.Options class and experiment with inPreferredConfig and inSampleSize
I would suggest that you either use the suggestion by DigCamara and decide on a size and load a downsampled image of nearly that size (I say nearly because you won't get the exact size using that technique) or that you try to load the full size image and then recursively increase the sample size (by factors of two for best result) until you either reach a max sample size or the image is loaded:
/**
* Load a bitmap from a stream using a specific pixel configuration. If the image is too
* large (ie causes an OutOfMemoryError situation) the method will iteratively try to
* increase sample size up to a defined maximum sample size. The sample size will be doubled
* each try since this it is recommended that the sample size should be a factor of two
*/
public Bitmap getAsBitmap(InputStream in, BitmapFactory.Config config, int maxDownsampling) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
options.inPreferredConfig = config;
Bitmap bitmap = null;
// repeatedly try to the load the bitmap until successful or until max downsampling has been reached
while(bitmap == null && options.inSampleSize <= maxDownsampling) {
try {
bitmap = BitmapFactory.decodeStream(in, null, options);
if(bitmap == null) {
// not sure if there's a point in continuing, might be better to exit early
options.inSampleSize *= 2;
}
}
catch(Exception e) {
// exit early if we catch an exception, for instance an IOException
break;
}
catch(OutOfMemoryError error) {
// double the sample size, thus reducing the memory needed by 50%
options.inSampleSize *= 2;
}
}
return bitmap;
}

Related

Bitmap decodeResource - out-of-memory crash on Samsung S5

I'm facing a crash every time with a Galaxy S5 when trying to show a background image.
This background is located in xxhdpi resource folder, the size is the same as the S5 screen (1080x1920) so I don't need to call "createScaledBitmap" for scaling it. The resolution of this image is JPG 96dpi.
And when calling decodeResource... crash!!! How is this possible? Is the only bitmap I'm loading in this "super-powerful" device.
Thanks!!!
Below my code (scale = 1 for S5):
public static Bitmap decodeBitmapFromResource(Resources res, int resId, float scale) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options,
(int)(options.outWidth*scale),
(int)(options.outHeight*scale));
options.inJustDecodeBounds = false;
if (scale > 1) {
Bitmap bitmap = BitmapFactory.decodeResource(res, resId);
return Bitmap.createScaledBitmap(bitmap, (int)(options.outWidth*scale),
(int)(options.outHeight*scale), true);
}
return BitmapFactory.decodeResource(res, resId, options);
}
i too faced this problem many times...
try using this code..
private Bitmap decodeFile(File f) throws IOException {
Bitmap b = null;
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay()
.getMetrics(metrics);
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
o.inDither = false; // Disable Dithering mode
o.inPurgeable = true; // Tell to gc that whether it needs free memory,
// the Bitmap can be cleared
o.inInputShareable = true;
FileInputStream fis = new FileInputStream(f);
BitmapFactory.decodeStream(fis, null, o);
fis.close();
int scale = 1;
if (o.outHeight > metrics.heightPixels
|| o.outWidth > metrics.widthPixels) {
scale = (int) Math.pow(
2,
(int) Math.ceil(Math.log(metrics.heightPixels
/ (double) Math.max(o.outHeight, o.outWidth))
/ Math.log(0.5)));
}
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = new FileInputStream(f);
b = BitmapFactory.decodeStream(fis, null, o2);
fis.close();
return b;
}
and take care of few things like make every bitmap null after its use etc.
try this
public static Bitmap decodeBitmapFromResource(String pathName, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
// return BitmapFactory.decodeResource(res, resId, options);
return BitmapFactory.decodeFile(pathName, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
Add this line in your Manifest file in the application tag. It doesn't solve the problem just allows your app to have more memory:
android:largeHeap="true"
UPDATE:
However using largeHeap is not a good solution. here is the google's doc about this.
However, the ability to request a large heap is intended only for a
small set of apps that can justify the need to consume more RAM (such
as a large photo editing app). Never request a large heap simply
because you've run out of memory and you need a quick fix—you should
use it only when you know exactly where all your memory is being
allocated and why it must be retained. Yet, even when you're confident
your app can justify the large heap, you should avoid requesting it to
whatever extent possible. Using the extra memory will increasingly be
to the detriment of the overall user experience because garbage
collection will take longer and system performance may be slower when
task switching or performing other common operations.
And about loading bitmaps:
When you load a bitmap, keep it in RAM only at the resolution you need
for the current device's screen, scaling it down if the original
bitmap is a higher resolution. Keep in mind that an increase in bitmap
resolution results in a corresponding (increase2) in memory needed,
because both the X and Y dimensions increase.
It's not bad to take a look at this page, it explains ways of managing memory:
How Your App Should Manage Memory
So I think my last answer is not a good solution and You might rethink your strategy in loading images. Hope this answer helps you ;)

How to load a high resolution Bitmap in Android?

I'm working on an android app, and in resources folder I have an image which is 8000x400px resolution. It is a .png that I'm using at my Sprite class to simulate the movement of an animal.
I display the portion of the png using drawBitmap() in my SurfaceView class.
Sprite class, SurfaceView and all the elements work perfect, but when working with that large images it doesnt display anything.
To fix this problem I would like to know.
What is the limit of maximum resolution of a Bitmap allowed in
Android?
How could I display in onDraw() a Sprite with that size?
Concerning the displaying / loading of Bitmaps:
You need to load the Bitmap properly and adjust the size of the Bitmap to your needs. In most cases, it makes no sense to load a Bitmap with higher resolution than the screen of the device supports.
Furthermore, this practice is very important to avoid OutOfMemoryErrors when working with Bitmaps that large.
As an example, a Bitmap with the size of 8000 x 4000 uses more than 100 Megabytes of RAM (in 32 Bit color), which is an enormous amount for a mobile device and much more than even high end devices are capable of handling.
This is how to load a Bitmap properly:
public abstract class BitmapResLoader {
public static Bitmap decodeBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
private static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
}
Example usage in code:
Bitmap b = BitmapResLoader.decodeBitmapFromResource(getResources(),
R.drawable.mybitmap, 500, 500);
Taken from the Google Android Developer guidelines here:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Concerning the maximum Bitmap size:
The maximum Bitmap size limit depends on the unterlying OpenGL implementation. When using OpenGL, this can be tested via (source: Android : Maximum allowed width & height of bitmap):
int[] maxSize = new int[1];
gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxSize, 0);
e.g. for the Galaxy S2, it is 2048x2048.

How to load large images in Android and avoiding the out of memory error?

I'm working on an app that uses large images (1390 × 870 : 150kb - 50kb). I'm adding images as I tap a trigger/ImageView.
At a certain point I'm getting an out of memory error:
java.lang.OutOfMemoryError
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:613)
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:378)
To resize the image I'm doing this:
Bitmap productIndex = null;
final String imageLoc = IMAGE_LOCATION;
InputStream imageStream;
try {
imageStream = new FileInputStream(imageLoc);
productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400);
productIV.setImageBitmap(productIndex);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, String resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(resId, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 3;
final int halfWidth = width / 3;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
I got this way of resizing to save space from the Android Docs:
Loading Large Bitmaps Efficiently
According to the log this like is the culprit in the decodeSampledBitmapFromResource method :
return BitmapFactory.decodeFile(resId, options);
----- edit -----
Here is how I'm adding each item to the FrameLayout.
for(int ps=0;ps<productSplit.size();ps++){
//split each product by the equals sign
List<String> productItem = Arrays.asList(productSplit.get(ps).split("="));
String tempCarID = productItem.get(0);
tempCarID = tempCarID.replace(" ", "");
if(String.valueOf(carID).equals(tempCarID)){
ImageView productIV = new ImageView(Configurator.this);
LayoutParams productParams = new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
productIV.setId(Integer.parseInt(partIdsList.get(x)));
productIV.setLayoutParams(productParams);
final String imageLoc = productItem.get(2);
InputStream imageStream;
try {
imageStream = new FileInputStream(imageLoc);
productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400);
productIV.setImageBitmap(productIndex);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
productLayers.addView(productIV);
}
}
You can use another bitmap-config to heavily decrease the size of the images. The default is RGB-config ARGB8888 which means four 8-bit channels are used (red, green, blue, alhpa). Alpha is transparency of the bitmap. This occupy a lot of memory - imagesize X 4. So if the imagesize is 4 megapixel 16 megabytes will immidiately be allocated on the heap - quickly exhausting the memory.
Instead - use RGB_565 which to some extent deteriorate the quality - but to compensate this you can dither the images.
So - to your method decodeSampledBitmapFromResource - add the following snippets:
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
In your code:
public static Bitmap decodeSampledBitmapFromResource(Resources res, String resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
return BitmapFactory.decodeFile(resId, options);
}
References:
http://developer.android.com/reference/android/graphics/Bitmap.Config.html#ARGB_8888
High resolution devices such as S4 usually run out of memory if you do not have your image in the proper folder which is drawable-xxhdpi. You can also put your image into drawable-nodpi. The reason it would run out of memorey if your image just in drawable that the android would scale the image thinking that the image was designed for low resolution.
You can use this beautiful library https://github.com/davemorrissey/subsampling-scale-image-view
Here is how I'm adding each item to the FrameLayout
that's the problem, the code keep adding and adding more images, and doesn't matter how well you resize or how much memory the device have, at certain point it WILL run out of memory. That's because every image you add it's keeping in memory.
For this type of situation what the apps do is to use a ViewGroup that can recycle views. I don't know your layout, but usually is a ListView, GridView or a ViewPager, by recycling views you re-use the layout and can dispose re-load images as necessary.
For the specific purpose of loading and resizing images I strongly advise use Picasso library as it is VERY well written, simple to use and stable.
You are still going to need to manage the bitmap memory as I wouldn't try to allocate a total space more than 3x the size of the screen (if you think about it makes sense for scrolling behavior). If you are overlaying one image on top of another, at some point, you're hitting an Out Of Memory error. You may need to look at capturing the prior screen image as a single background image to make sure you still fit within the available memory. Or when a new image overlaps an existing image only load and render the visible portion. If performance becomes an issue, then you may need to consider OpenGL Textures but the memory allocation problem is still the same.
Do go through all of the Displaying Bitmaps Training as it should give you some additional ideas of how to handle display.
Use Fresco library to load large images will avoid this error.
in xml layout
<com.facebook.drawee.view.SimpleDraweeView
android:id="#+id/my_image_view"
android:layout_width="1300dp"
android:layout_height="1300dp"
fresco:placeholderImage="#drawable/my_drawable"
/>
and in javacode
Uri uri = Uri.parse("https://image.png");
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);

BitmapFactory.decodeFile out of memory with images 2400x2400

I need to send an image from a file to a server. The server request the image in a resolution of 2400x2400.
What I'm trying to do is:
1) Get a Bitmap using BitmapFactory.decodeFile using the correct inSampleSize.
2) Compress the image in JPEG with a quality of 40%
3) Encode the image in base64
4) Sent to the server
I cannot achieve the first step, it throws an out of memory exception. I'm sure the inSampleSize is correct but I suppose even with inSampleSize the Bitmap is huge (around 30 MB in DDMS).
Any ideas how can do it? Can I do these steps without created a bitmap object? I mean doing it on filesystem instead of RAM memory.
This is the current code:
// The following function calculate the correct inSampleSize
Bitmap image = Util.decodeSampledBitmapFromFile(imagePath, width,height);
// compressing the image
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 40, baos);
// encode image
String encodedImage = Base64.encodeToString(baos.toByteArray(),Base64.DEFAULT));
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromFile(String path,int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path,options);
}
you can skip ARGB_8888, and use RGB_565 instead, and then dither then images to preserve good quality
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
you have to use BitmapFactory.Options with inJustDecodeBounds set to true. This way you can load information about the bitmap and calculate the value for downsampling it (inSampleSize for instance)
Do NOT load the image as a bitmap, convert it to an array, then send it.
instead:
Read it as a file, in JPG format. Use the files byte array, to encode it, and send the file across.
Loading it to bitmap, is going to cause huge memory issues unnecessarily. An image, reperesented in Bitmap format, will take ~20x or more memory than neccessary.
On the server side, you will need to treat it as a file too. rather than a bitmap.
Here is a link to loading a file to byte[] : Elegant way to read file into byte[] array in Java

High resolution Image - OutOfMemoryError

I am developing an application for the Galaxy S4.
One of the requirements of the application is having a SplashScreen containing an Image of 1920x1080 pixels. Its a high quality .jpeg image and the size of the Image is about 2 Megabytes.
The problem is that I am getting an OutOfMemoryError as soon as I start the Application. I am quite stunned that this already happens with an image of just 2 megabyte in size? How can I fix this problem and display the image?
Changing the dimensions or the size of the image is not an option.
SplashScreen.java
public class Splashscreen extends Activity {
private static final int SPLASH_DURATION = 2000;
private boolean isBackPressed = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(LayoutParams.FLAG_FULLSCREEN, LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.splashscreen);
Handler h = new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
// check if the backbutton has been pressed within the splash_duration
if(!isBackPressed) {
Intent i = new Intent(Splashscreen.this, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Splashscreen.this.startActivity(i);
overridePendingTransition(R.anim.short_fade_in, R.anim.short_fade_out);
}
finish();
}
}, SPLASH_DURATION);
}
#Override
public void onBackPressed() {
isBackPressed = true;
super.onBackPressed();
}
}
And the splashscreen.xml
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ivSplashScreenImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/high_res_splashscreen_image"/>
ADDITIONAL INFORMATION:
Sometimes, (when a lot of device memory is available) the app is able to make it past the splash screen, but then, the memory consumption of the app is just insane. (around 100 megabyte). Even though I close the SplashScreen Activity and finish() it, it seems that there is a reference to the ImageView / the Image kept in memory.
How can I reduce the huge memory consumption?
When I do not display the splash screen, my app only consumes around 35MB
of memory. With the SplashScreen image, its around 100MB.
Three hints which should help you:
Use this to load your images, from Loading Large Bitmaps Android documentation:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
Make sure you have only one instance of your Bitmap in memory. After displaying it, call recycle() and set your reference to null. You can use Memory Allocation Tracker to see what is allocated. You can also read HPROF files, as suggested in comments.
By default ARGB_8888 pixel format is used, which means 4 bytes per pixel. Very good article: Bitmap quality, banding and dithering. Your image is JPEG, so it doesn't have transparency, so you are wasting 1 byte on every pixel for alpha channel. It's not very probable, but maybe with acceptable quality you can use even more economical format. Take a look at them. Maybe RGB_565 for example. It takes 2 bytes for pixel, so your image would be 50% lighter. You can enable dithering to improve the quality of RGB_565.
I had a similar problem and the solution is simple. Put your high resolution 1920x1080 px image in the drawable-nodpi directory. The system does not scale resources tagged with this qualifier regardless of the current screen's density, which leads to the OutOfMemoryError, so the problem should disappear.
Please check Android documentation: http://developer.android.com/intl/es/guide/practices/screens_support.html
Hope it helps.

Categories

Resources