During runtime, I am trying to put an image in the surface view. When I tried using the image from the Drawable folder I got Out of memory error. After a quick search in the stackoverflow, I found that there will be some relief if we access the image from the asset folder. But still I get the Out of memory error during runtime.
I have analyzed and found that scaling will help in resolving this kind of memory related issues. The thing is that I have the image size of 1280 x 720 and the device size also the same. Hence I feel like the scaling will not have any effect.
As we have experts in this community, I would appreciate if you can help me with some suggestions/examples to resolve this kind of issue.
Scenario 1:
Using the Bitmap from Drawable folder.
backgoundImage = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.backgroundhomepage), (int) dWidth, (int) dHeight, true);
/***********************************************************************************************************************************************************
1. To get the image from asset library
**************************************************************************************************************************************************************/
public Bitmap getAssetImage(Context context, String filename) throws IOException {
AssetManager assets = context.getResources().getAssets();
InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
Bitmap bitmap = BitmapFactory.decodeStream(buffer);
return bitmap;
}
Scenario 2:
Using the Bitmap from Assets folder
backgoundImage = Bitmap.createScaledBitmap(getAssetImage(context,"backgroundhomepage"), (int) dWidth, (int) dHeight, true);
OutofMemory occurs when your app exceeds memory allocated in heap. The bitmap is too large to fit in memory ie heap. In such a case you run out of memory. You need to scale down the bitmap and then use the same. For that check the link below
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html.
There is also a blog # http://android-developers.blogspot.in/2009/01/avoiding-memory-leaks.html (avoiding memory leaks)
public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HIGHT=HIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
Quoting from the docs
The BitmapFactory class provides several decoding methods (decodeByteArray(), decodeFile(), decodeResource(), etc.) for creating a Bitmap from various sources. Choose the most appropriate decode method based on your image data source. These methods attempt to allocate memory for the constructed bitmap and therefore can easily result in an OutOfMemory exception. Each type of decode method has additional signatures that let you specify decoding options via the BitmapFactory.Options class.
Setting the inJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting outWidth, outHeight and outMimeType. This technique allows you to read the dimensions and type of the image data prior to construction (and memory allocation) of the bitmap.
Also check this link for memory management.
https://www.youtube.com/watch?v=_CruQY55HOk
Got a quick Solution
<application
android:largeHeap="true" >
put into appplication tag in manifest file.
You can use the following code to load the bitmap from file:
private Bitmap decodeFile(File f,int req_Height,int req_Width){
try {
//decode image size
BitmapFactory.Options o1 = new BitmapFactory.Options();
o1.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o1);
//Find the correct scale value. It should be the power of 2.
int width_tmp = o1.outWidth;
int height_tmp = o1.outHeight;
int scale = 1;
if(width_tmp > req_Width || height_tmp > req_Height)
{
int heightRatio = Math.round((float) height_tmp / (float) req_Height);
int widthRatio = Math.round((float) width_tmp / (float) req_Width);
scale = heightRatio < widthRatio ? heightRatio : widthRatio;
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
o2.inScaled = false;
return BitmapFactory.decodeFile(f.getAbsolutePath(),o2);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
It should resolve your out of memory exception.
Link here has a good detailed explanation of your answer.
Related
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 ;)
What does BitmapFactory.Options in android.graphics.BitmapFactory.Options do?
There is no theoretical explanation in the android sdk reference manual about this class, it only contains the explanation about the methods of the class.
Bitmapfactory is mainly used for Scaling
Bitmap lBmp = BitmapFactory.decodeResource(getResources(), R.Drawable.ic_dolphin);
It gets the "dolpin" image and it will reduce the image size, if we dnt use bitmapfactory then it leads to insufficient memory allocations
It's used to pass options to the BitmapFactory - as you might expect :)
For example, you can use it to explicitly scale the Bitmap up or down from the source.
See this example
This method is used to create bitmap of given specific size which is stored in sdcard.
public Bitmap decodeFile(String path,int size) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, o);
// The new size we want to scale to
final int REQUIRED_SIZE = size;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeFile(path, o2);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
When I try to get image from camera or gallery, I get error. Here is a part of logcat:
06-27 05:51:47.297: E/dalvikvm-heap(438): Out of memory on a 35295376-byte allocation.
06-27 05:51:47.312: E/dalvikvm(438): Out of memory: Heap Size=108067KB, Allocated=71442KB, Limit=131072KB
06-27 05:51:47.312: E/dalvikvm(438): Extra info: Footprint=108067KB, Allowed Footprint=108067KB, Trimmed=56296KB
06-27 05:51:47.312: E/PowerManagerService(438): Excessive delay when setting lcd brightness: mLcdLight.setBrightness(176, 1) spend 288ms, mask=2
06-27 05:51:48.052: E/dalvikvm-heap(4332): Out of memory on a 24023056-byte allocation.
06-27 05:51:48.057: E/dalvikvm(4332): Out of memory: Heap Size=63139KB, Allocated=40922KB, Limit=65536KB
06-27 05:51:48.057: E/dalvikvm(4332): Extra info: Footprint=63139KB, Allowed Footprint=63139KB, Trimmed=0KB
06-27 05:51:48.057: E/EmbeddedLogger(438): App crashed! Process: <my_app_name>
Here is my code that provides me to take an image:
Intent pickIntent = new Intent();
pickIntent.setType("image/*");
pickIntent.setAction(Intent.ACTION_GET_CONTENT);
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Intent chooserIntent = Intent.createChooser(pickIntent, "Select or take a new Picture");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { takePhotoIntent });
startActivityForResult(chooserIntent, selectPic);
and at onActivityResult() I do:
Bitmap bitmapSelectedImage = null;
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
cursor.moveToFirst();
String filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
cursor.close();
bitmapSelectedImage = BitmapFactory.decodeFile(filePath); // Here is where do I get error.
I get error on bitmapSelectedImage = BitmapFactory.decodeFile(filePath); line.
I have looked a lot websites/topics for it but no one could helped.
Any suggestions?
your memory allocation heap size is something very limited.
trying loading high resolution image from file to the heap can easily cause out of memory error.
assuming that the camera app really taking a very high resolution (almost sure that is the case), you should load to memory only scaled version of the bitmap in the size required for displaying.
the document you already been suggested to see - http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
provides full functionall methods to do exactly that.
1) first step is calculating (without loading to memory) the required scale.
that's the calculateInSampleSize method.
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 smaller than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
2) second step is the full method using step 1:
public static Bitmap getSampleBitmapFromFile(String bitmapFilePath, int reqWidth, int reqHeight) {
// calculating image size
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(new File(bitmapFilePath)), null, options);
int scale = calculateInSampleSize(options, reqWidth, reqHeight);
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(new File(bitmapFilePath)), null, o2);
}
reqHeight and reqWith are the hight and width in pixels of the image view that displaying the image.
so let's say your image view is 100x100 pixels, all you need to do is:
bitmapSelectedImage = getSampleBitmapFromFile(filePath, 100, 100);
If bitmap sizes are very big or inefficently handled then This problems occurs. To decode bitmap efficiently you can check these tips in android developers site.
Displaying Bitmaps Efficiently
Do not use strong references for bitmaps. Use it as below. Always Use weakreferences so that system can gc the object and reduce the memory leaks
WeakReference<Bitmap> bitmapSelectedImage ;
You need to scale down your image.
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html.
Use appropriate Bitmap.decode method and scale down the image.
Bitmap.decode
Example :
Call the method with the required parameters.
public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HIGHT=HIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
This code worked for me :try this
Bitmap ThumbImage = ThumbnailUtils.extractThumbnail(bitmap, 100, 100);
Ok Someone help me figure this out. I am using the Bitmap.options as recommended by other threads and android tutorials to figure out the inSample size. The problem that the following code is resulting in null bitmap instead of scaled bitmap
private int determineCorrectScale(InputStream imageStream){
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(imageStream, null, o);
// The new size we want to scale to
final int REQUIRED_SIZE = 100;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE) {
scale *= 2;
}
return scale;
}
private String saveScaledBitmapToPhone(Uri imagUri){
InputStream imageStream;
try {
imageStream = getContentResolver().openInputStream(imagUri);
int scale= determineCorrectScale(imageStream);
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = scale;
Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream, null, options );
.
.
.
.
} catch (Exception e) {
return imagUri.toString(); //default
}
}
The problem that yourSelectedImage is null. However if I comment out the line
int scale= determineCorrectScale(imageStream);
and set the insampleSize to 8 or 16 or any other fixed manual number then everything works fine. Can any one explain this behaviour or how to fix it? My feeling says it is due to creating two Options objects of static class but that's just a guess. I still can't fix it :(
PLEASE HELP
You're reusing the same data stream. Either reset it, cache the data in a byte array, or open a new stream.
First off, I have read many posts and articles about out of memory exceptions but none of them have helped with my situation. What I'm trying to do is load an image from the sd card but scale it to an exact pixel size.
I first get the width and height of the image and calculate the sample size:
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(backgroundPath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, getWidth(), getHeight());
Here's how I get the sample size (although its not really relevant):
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;
// NOTE: we could use Math.floor here for potential better image quality
// however, this also results in more out of memory issues
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
Now that I have a sample size I load the image from disk to an approximate size (sample size):
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
options.inPurgeable = true;
Bitmap bmp = BitmapFactory.decodeFile(backgroundPath, options);
Now, I scale this bitmap that I have created to the exact size I need and clean up:
// scale the bitmap to the exact size we need
Bitmap editedBmp = Bitmap.createScaledBitmap(bmp, (int) (width * scaleFactor), (int) (height * scaleFactor), true);
// clean up first bitmap
bmp.recycle();
bmp = null;
System.gc(); // I know you shouldnt do this, but I'm desperate
The above step is usually get my out of memory exception. Does anyone know a way to load an exact size bitmap from disk to avoid having to create two separate bitmaps like above?
Also, it seems like more exceptions occur when the user runs this code for a second time (sets a new image). However, I make sure to unload the drawable that was created from the bitmap which allows it to be garbage collected before this code is run again.
Any suggestions?
Thanks,
Nick
In your case there's no need to create the intermediate bitmap after you've performed the first decode. Since you're drawing to to a Canvas, you can use either the following methods (whichever you find most convenient) to scale the image to the perfect size.
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
Maybe this method would be helpful, I think I pulled it off of stackoverflow myself. It solved my out of memory exception issue.
private Bitmap decodeFile(File f){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_SIZE=250;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}