I put 4x4 imageView to an activity(BoardActivity), and user can change the images by clicking them. With HTC Desire (Android 2.2.2), I got OOM(Out Of Memory) in about 30 minutes of intensive useage -EDIT: 16th start of this activity-, but no other devices produces this (android 2.1, and android 2.2.1). Is it possible, that I made some mistake with the bitmap/imageview useage and that causes this error?
First, I load all resource ID into a map:
private Map<String, Integer> imageResourceMap;
imageResourceMap = new HashMap<String, Integer>();
imageResourceMap.put("a", R.drawable.a);
imageResourceMap.put("b", R.drawable.b);
imageResourceMap.put("c", R.drawable.c);
//... I store 55 drawable's resourceId in this map
Then I resize and save every image into bitmap, represented in Map:
private static Map<String, Bitmap> imageBitmap;
private void loadBitmaps(int imagesSize) {
for (String s : imageResourceMap.keySet()) {
Bitmap tmp = getBitmapFromRes(imageResourceMap.get(s), imagesSize);
imageBitmap.put(s, tmp);
}
}
private Bitmap getBitmapFromRes(int resId, int imageSize) {
Bitmap b = null;
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
InputStream fis = getResources().openRawResource(resId);
BitmapFactory.decodeStream(fis, null, o);
fis.close();
int scale = 1;
if (o.outHeight > imageSize || o.outWidth > imageSize) {
scale = (int) Math.pow(2, (int) Math.round(Math.log(imageSize / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
}
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = getResources().openRawResource(resId);
b = BitmapFactory.decodeStream(fis, null, o2);
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return b;
}
I keep the imageViews in an array, and init all images with this function:
private static ImageView[][] imageViews;
private ImageView getImage(String name) {
MyImageView item = new MyImageView(this, i, j, c + "");
item.setImageBitmap(imageBitmap.get(name));
item.setAdjustViewBounds(true);
return item;
}
When I need to change an image, I simple change its resource:
imageViews[i][j].setImageBitmap(imageBitmap.get("a"));
And right before I finish the activity, I recycle the bitmap map:
private void recycleImageBitmaps() {
for (Bitmap b : imageBitmap.values()) {
if (b != null) {
b.recycle();
}
}
}
In AndroidManifest.xml I declared this activity "singleTask":
<activity android:name=".game.board.BoardActivity" android:launchMode="singleTask">
</activity>
In this application(game), we reopen this activity a several times...
What did I wrong? Can this cause the Out Of Memory error?
CORRECTION
Corrected the getBitmapFromRes like this:
private Bitmap getBitmapFromRes(int resId, int imageSize) {
Bitmap tmp = null;
Bitmap b = null;
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
InputStream fis = getResources().openRawResource(resId);
tmp = BitmapFactory.decodeStream(fis, null, o);
fis.close();
int scale = 1;
if (o.outHeight > imageSize || o.outWidth > imageSize) {
scale = (int) Math.pow(2, (int) Math.round(Math.log(imageSize / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
}
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = getResources().openRawResource(resId);
b = BitmapFactory.decodeStream(fis, null, o2);
fis.close();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(tmp != null){
tmp.recycle();
tmp = null;
}
}
return b;
}
HTC still crashed at the 11th start of this activity.
EDIT:
This activity(BoardActivity) launch from an Activity(MenuActivity), which have 4 imageButton, and is in a Tabhost activity. The imageButtons declarations look like this:
<ImageButton
android:id="#+id/game_menu_CreateButton"
android:layout_width="120dip"
android:layout_height="120dip"
android:layout_alignRight="#+id/textView1"
android:layout_alignTop="#+id/textView1"
android:background="#drawable/create"
android:layout_marginRight="1sp"
android:layout_marginTop="10sp"
/>
When I start the BoardActivity from MenuActivity, I don't call finish() at MenuActivity, and when I call finish() at the BoardActivity, I don't start a new intent, so it just return to the already opened MenuActivity. And the 16 round of this, I got the OOM.
To reduce memory, you can try out these things :
After converting drawables into bitmaps, you can set the imageResourceMap to null. This will unload the 3 drawables.
Avoid storing a reference to the imageViews. You might be storing imageViews even after they are removed from the UI
Recycle the bitmaps more often. Instead of just onDestroy, as soon as you know that one bitmap is not used, you can recycle it.
Edit : based on the conversation in the comments : The bitmap returned by BitmapFactory.decodeStream(fis, null, o) is not assigned to any variable and hence is not recycled. Android 2.2 and 2.3 will have leaks in this line.
Related
My app is an OCR app base on Tesseract. It will do OCR task from camera picture. Users can take many pictures and put them into an OCR queue. To get more accuracy, I want to keep high quality image (I choose min size is 1024 x 768 (maybe larger in future), JPEG, 100% quality). When users take many pictures, there are three things to do:
Save the image data byte[] to file and correct EXIF.
Correct the image orientation base on device's orientation. I know there are some answers that said the image which comes out of the camera is not oriented automatically, have to correct it from file, like here and here. I'm not sure about it, I can setup the camera preview orientation correctly, but the image results aren't correct.
Load bitmap from taken picture, convert it to grayscale and save to another file for OCR task.
And here is my try:
public static boolean saveBitmap(byte[] bitmapData, int orientation, String imagePath, String grayScalePath) throws Exception {
Boolean rotationSuccess = false;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap originalBm = null;
Bitmap bitmapRotate = null;
Bitmap grayScale = null;
FileOutputStream outStream = null;
try {
// save directly from byte[] to file
saveBitmap(bitmapData, imagePath);
// down sample
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
int sampleSize = calculateInSampleSize(options, Config.CONFIG_IMAGE_WIDTH, Config.CONFIG_IMAGE_HEIGHT);
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
originalBm = BitmapFactory.decodeFile(imagePath, options);
Matrix mat = new Matrix();
mat.postRotate(orientation);
bitmapRotate = Bitmap.createBitmap(originalBm, 0, 0, originalBm.getWidth(), originalBm.getHeight(), mat, true);
originalBm.recycle();
originalBm = null;
outStream = new FileOutputStream(new File(imagePath));
bitmapRotate.compress(CompressFormat.JPEG, 100, outStream);
// convert to gray scale
grayScale = UIUtil.convertToGrayscale(bitmapRotate);
saveBitmap(grayScale, grayScalePath);
grayScale.recycle();
grayScale = null;
bitmapRotate.recycle();
bitmapRotate = null;
rotationSuccess = true;
} catch (OutOfMemoryError e) {
e.printStackTrace();
System.gc();
} finally {
if (originalBm != null) {
originalBm.recycle();
originalBm = null;
}
if (bitmapRotate != null) {
bitmapRotate.recycle();
bitmapRotate = null;
}
if (grayScale != null) {
grayScale.recycle();
grayScale = null;
}
if (outStream != null) {
try {
outStream.close();
} catch (IOException e) {
}
outStream = null;
}
}
Log.d(TAG,"save completed");
return rotationSuccess;
}
Save to file directly from byte[]
public static void saveBitmap(byte[] bitmapData, String fileName) throws Exception {
File file = new File(fileName);
FileOutputStream fos;
BufferedOutputStream bos = null;
try {
final int bufferSize = 1024 * 4;
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos, bufferSize);
bos.write(bitmapData);
bos.flush();
} catch (Exception ex) {
throw ex;
} finally {
if (bos != null) {
bos.close();
}
}
}
Calculate scale size
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 / 2;
final int halfWidth = width / 2;
// 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;
}
When save complete, this image is loaded into thumbnail image view by UIL. The problem is the save task is very slow (wait some second before save complete and load into view), and sometime I got OutOfMemory exception. Is there any ideas to reduce the save task and avoid OutOfMemory exception?
Any help would be appreciated!
P/S: the first time I try to convert byte[] to bitmap instead of save to file, and then rotate and convert to grayscale, but I still got above issues.
Update: here is the grayscale bitmap process:
public static Bitmap convertToGrayscale(Bitmap bmpOriginal) {
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmpGrayscale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bmpOriginal, 0, 0, paint);
return bmpGrayscale;
}
The OutOfMemory exception seldom occurred (just a few times) and I can't reproduce it now.
Update:
Since you're still saying that the method takes too long time I would define a callback interface
interface BitmapCallback {
onBitmapSaveComplete(Bitmap bitmap, int orientation);
onBitmapRotateAndBWComlete(Bitmap bitmap);
}
Let your activity implement the above interface and convert the byte[] to bitmap in top of your saveBitmap method and fire the callback, before the first call to save. Rotate the imageView based on the orientation parameter and set a black/white filter on the imageView to fool the user into thinking that the bitmap is black and white (do this in your activity). See to that the calls are done on main thread (the calls to imageView). Keep your old method as you have it. (all steps need to be done anyway) Something like:
public static boolean saveBitmap(byte[] bitmapData, int orientation, String imagePath, String grayScalePath, BitmapCallback callback) throws Exception {
Boolean rotationSuccess = false;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap originalBm = null;
Bitmap bitmapRotate = null;
Bitmap grayScale = null;
FileOutputStream outStream = null;
try {
// TODO: convert byte to Bitmap, see to that the image is not larger than your wanted size (1024z768)
callback.onBitmapSaveComplete(bitmap, orientation);
// save directly from byte[] to file
saveBitmap(bitmapData, imagePath);
.
.
// same as old
.
.
saveBitmap(grayScale, grayScalePath);
// conversion done callback with the real fixed bitmap
callback.onBitmapRotateAndBWComlete(grayScale);
grayScale.recycle();
grayScale = null;
bitmapRotate.recycle();
bitmapRotate = null;
rotationSuccess = true;
How do you setup your camera? What might be causing the long execution time in the first saveBitmap call, could be that you are using the default camera picture size settings and not reading the supported camera picture size and choosing best fit for your 1024x768 image needs. You might be taking big mpixel images and saving such, but in the end need you need < 1 mpixles (1024x768). Something like this in code:
Camera camera = Camera.open();
Parameters params = camera.getParameters();
List sizes = params.getSupportedPictureSizes();
// Loop camera sizes and find best match, larger than 1024x768
This is probably where you will save most of the time if you are not doing this already. And do it only once, during some initialization phase.
Increase the buffer to 8k in saveBitmap, change the 1024*4 to 1024*8, this would increase the performance at least, not save any significant time perhaps.
To save/reuse bitmap memory consider using inBitmap field, if you have a post honeycomb version, of BitmapFactory.Options and set that field to point to bitmapRotate bitmap and send options down to your convertToGrayscale method to not need allocating yet another bitmap down in that method. Read about inBitmap here: inBitmap
I have 2 ImageView on my home layout and their content comes from images placed on SD card as shown in below code snippet:
try {
String tempPath1 = Environment.getExternalStorageDirectory()
+ File.separator + "Clipping_Pictures" + File.separator
+ "06-05-2013_02-06-09pm.png";
File f = new File(tempPath1);
Bitmap b = null, b2 = null;
b = BitmapFactory.decodeFile(f.getPath());
if (f.exists()) {
ivClip1.setImageBitmap(b);//ivClip1 is ImageView
}
tempPath1 = Environment.getExternalStorageDirectory()
+ File.separator + "Clipping_Pictures" + File.separator
+ "06-05-2013_02-06-33pm.png";
f = new File(tempPath1);
b2 = BitmapFactory.decodeFile(f.getPath());
if (f.exists()) {
ivClip2.setImageBitmap(b2);
}
} catch (Exception e) {
e.printStackTrace();
}
When I load the app for the 1st time, it displays both the images on respective imageviews. But 2nd launch on-wards, app crashes with following exception:
OutOfMemoryError: bitmap size exceeds VM budget
Note that two resource images are .png and of size ~850kb each which should be fine I guess.
There are similar threads on SO and on internet and I tried some of their suggested solutions, but none seems to work.
Any help appreciated.
If you are building your app for Android 3.0 on wards, then you can use android:largeHeap="true" attribute in your application tag of manifest file.
Doing this, hope your app won't crash due to Out of Memory.
Here is example:
application
android:allowBackup="true"
android:icon="#drawable/icon_96x96"
android:label="#string/app_name"
android:largeHeap="true"
android:theme="#android:style/Theme.NoTitleBar"
Thanks!
its because of large size of your bitmaps. compress your bitmap using following code:
Bitmap ShrinkBitmap(byte[] file, int width, int height){
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeByteArray(file, 0, file.length, bmpFactoryOptions);
int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)height);
int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)width);
if (heightRatio > 1 || widthRatio > 1)
{
if (heightRatio > widthRatio)
{
bmpFactoryOptions.inSampleSize = heightRatio;
} else {
bmpFactoryOptions.inSampleSize = widthRatio;
}
}
bmpFactoryOptions.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeByteArray(file, 0, file.length, bmpFactoryOptions);
return bitmap;
}
Are you executing all this code from onCreate() or from onResume()?
You may try to clean the views before you try to load the images again (ivClip1.setImageBitmap(null) or a lightweight one), because while you are decoding both bitmaps you are still having the previous instances in memory while showing.
You can add this lines to resize bitmap and then use it
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file),null,options);
Method to calculate samplesize and reduce the bitmap size
private Bitmap decodeFile(File f){
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
final int REQUIRED_SIZE=70;
//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;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
I am working on an android application. The application has a view containing lots of image. I had an error, I will try to give as much information as possible hoping someone can give me some suggestions.
The application was working great on all the local testings. However, I received lots of crashes from users: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
This is the stack trace
0 java.lang.OutOfMemoryError: bitmap size exceeds VM budget
1 at android.graphics.Bitmap.nativeCreate(Native Method)
2 at android.graphics.Bitmap.createBitmap(Bitmap.java:507)
3 at android.graphics.Bitmap.createBitmap(Bitmap.java:474)
4 at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:379)
5 at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:498)
6 at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:473)
7 at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336)
8 at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:359)
9 at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:385)
My biggest problem is that I was not able to reproduce the issue locally even on old devices.
I have implemented lots of things to try to resolve this:
No memory leaks: I made sure there is no memory leaks at all. I removed the views when I dont need them. I also recycled all the bitmaps and made sure the garbage collector is working as it should. And I implemented all the necessary steps in the onDestroy() method
Image size scaled correctly: Before getting the image I get its dimension and calculate the inSampleSize.
Heap size: I also detect the Max Heap size before getting the image and make sure there is enough space. If there is not enough I rescale the image accordingly.
Code to calculate the correct inSampleSize
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)
{
if(width > height)
{
inSampleSize = Math.round((float) height / (float) reqHeight);
}
else
{
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
Code to get the bitmap
// decodes image and scales it to reduce memory consumption
private static Bitmap decodeFile(File file, int newWidth, int newHeight)
{// target size
try
{
Bitmap bmp = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), Uri.fromFile(file));
if(bmp == null)
{
// avoid concurrence
// Decode image size
BitmapFactory.Options option = new BitmapFactory.Options();
// option = getBitmapOutput(file);
option.inDensity = res.getDisplayMetrics().densityDpi < DisplayMetrics.DENSITY_HIGH ? 120 : 240;
option.inTargetDensity = res.getDisplayMetrics().densityDpi;
if(newHeight > 0 && newWidth > 0)
option.inSampleSize = calculateInSampleSize(option, newWidth, newWidth);
option.inJustDecodeBounds = false;
byte[] decodeBuffer = new byte[12 * 1024];
option.inTempStorage = decodeBuffer;
option.inPurgeable = true;
option.inInputShareable = true;
option.inScaled = true;
bmp = BitmapFactory.decodeStream(new FileInputStream(file), null, option);
if(bmp == null)
{
return null;
}
}
else
{
int inDensity = res.getDisplayMetrics().densityDpi < DisplayMetrics.DENSITY_HIGH ? 120 : 240;
int inTargetDensity = res.getDisplayMetrics().densityDpi;
if(inDensity != inTargetDensity)
{
int newBmpWidth = (bmp.getWidth() * inTargetDensity) / inDensity;
int newBmpHeight = (bmp.getHeight() * inTargetDensity) / inDensity;
bmp = Bitmap.createScaledBitmap(bmp, newBmpWidth, newBmpHeight, true);
}
}
return bmp;
}
catch(Exception e)
{
Log.e("Error calling Application.decodeFile Method params: " + Arrays.toString(new Object[]{file }), e);
}
return null;
}
Code to calculate image size based on Heap size for older devices
private void calculateImagesSize()
{
// only for android older than HoneyComb that does not support large heap
if(Build.VERSION.SDK_INT < Constants.HONEYCOMB)
{
long maxHeapSize = Runtime.getRuntime().maxMemory();
long maxImageHeap = maxHeapSize - 10485760;
if(Application.getResource().getDisplayMetrics().densityDpi >= DisplayMetrics.DENSITY_XHIGH)
{
maxImageHeap -= 12 * 1048576;
}
if(maxImageHeap < (30 * 1048576))
{
int screenHeight = Math.min(Application.getResource().getDisplayMetrics().heightPixels, Application.getResource()
.getDisplayMetrics().widthPixels);
long maxImageSize = maxImageHeap / 100;
long maxPixels = maxImageSize / 4;
long maxHeight = (long) Math.sqrt(maxPixels / 1.5);
if(maxHeight < screenHeight)
{
drawableHeight = (int) maxHeight;
drawableWidth = (int) (drawableHeight * 1.5);
}
}
}
}
I think the problem is with the Heap, maybe sometimes the os doesn't allow the application to use the maxheapsize. Also my biggest problem is that I was not able to reproduce the issue, so when I try a fix I have to wait a little to see if users are still getting the error.
What more could I try to avoid Out of memory issues? Any suggestions would be greatly appreciated. Thanks a lot
just use this function to decode...this is perfect solution for your error..because i also getting same error and i got this solution..
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;
}
Hi you have to decode the file . for this try with the following method.
public static Bitmap new_decode(File f) {
// 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; // Which kind of reference will be used to
// recover the Bitmap data after being
// clear, when it will be used in the future
try {
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 300;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 1.5 < REQUIRED_SIZE && height_tmp / 1.5 < REQUIRED_SIZE)
break;
width_tmp /= 1.5;
height_tmp /= 1.5;
scale *= 1.5;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
// o2.inSampleSize=scale;
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; // Which kind of reference will be used to
// recover the Bitmap data after being
// clear, when it will be used in the future
// return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
try {
// return BitmapFactory.decodeStream(new FileInputStream(f), null,
// null);
Bitmap bitmap= BitmapFactory.decodeStream(new FileInputStream(f), null, null);
System.out.println(" IW " + width_tmp);
System.out.println("IHH " + height_tmp);
int iW = width_tmp;
int iH = height_tmp;
return Bitmap.createScaledBitmap(bitmap, iW, iH, true);
} catch (OutOfMemoryError e) {
// TODO: handle exception
e.printStackTrace();
// clearCache();
// System.out.println("bitmap creating success");
System.gc();
return null;
// System.runFinalization();
// Runtime.getRuntime().gc();
// System.gc();
// decodeFile(f);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
By Reducing/Scale size of the Image you can get rid out of the Out of Memory Exception,
Try this
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 6;
Bitmap receipt = BitmapFactory.decodeFile(photo.toString(),options); //From File You can customise on your needs.
I wrote a summary of suggestions in another StackOverFlow question: Android: BitmapFactory.decodeStream() out of memory with a 400KB file with 2MB free heap
actually the problem is with the development os. In android unlike iOS , google people develop this based on camera resolution. Bitmaps take up a lot of memory, especially for rich images like photographs.Different cameras captures images with different pixels(different mobiles have different camera pixel capacity). Here in android based on that pixels only the captured image will take memory. so obviously a high resolution image will not uploaded by a phone with low pixel capacity.
In android os allocates utmost 16MB to every application. If the uploaded image takes more than this then java.lang.OutofMemoryError: bitmap size exceeds VM budget will occur and application crashes.
refer this
http://developer.android.com/training/displaying-bitmaps/index.html
If u want to avoid OOM, u can catch OOM and increase the sampleSize until the image can be resolved:
private Bitmap getBitmapSafely(Resources res, int id, int sampleSize) {
// res = context.getResources(), id = R.drawable.yourimageid
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inSampleSize = sampleSize;
try {
bitmap = BitmapFactory.decodeResource(res,
id, options);
} catch (OutOfMemoryError oom) {
Log.w("ImageView", "OOM with sampleSize " + sampleSize, oom);
System.gc();
bitmap = getBitmapSafely(res, id, sampleSize + 1);
}
return bitmap;
}
Hope it helps.
It is not suitable to catch the Error, just a workaround.
I trying to resize one image from InputStream, so I use the code in Strange out of memory issue while loading an image to a Bitmap object but I don't know why this code always return Drawable without image.
This one works well:
private Drawable decodeFile(InputStream f){
try {
InputStream in2 = new BufferedInputStream(f);
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=2;
return new BitmapDrawable(BitmapFactory.decodeStream(in2, null, o2));
} catch (Exception e) {
return null;
}
}
This one does not work:
private Drawable decodeFile(InputStream f){
try {
InputStream in1 = new BufferedInputStream(f);
InputStream in2 = new BufferedInputStream(f);
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in1,null,o);
//The new size we want to scale to
final int IMAGE_MAX_SIZE=90;
//Find the correct scale value. It should be the power of 2.
int scale = 2;
if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
scale = (int)Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE /
(double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inJustDecodeBounds = false;
o2.inSampleSize=scale;
return new BitmapDrawable(BitmapFactory.decodeStream(in2, null, o2));
} catch (Exception e) {
return null;
}
}
why one option affect the other? how its possible if I use two different InputStream and Options?
Actually you have two different BufferedInputStream but they internally use the only one InputStream object because BufferedInputStream is only a wrapper for InputStream.
So you can't just call two times BitmapFactory.decodeStream method on the same stream, it will definitely fail because the second time it wouldn't start decoding from the beginning of the stream. You need to reset your stream if it is supported or reopen it.
this is my code that works well, I hope this will help
//Decode image size
BitmapFactory.Options optionsIn = new BitmapFactory.Options();
optionsIn.inJustDecodeBounds = true; // the trick is HERE, avoiding memory leaks
BitmapFactory.decodeFile(filePath, optionsIn);
BitmapFactory.Options optionsOut = new BitmapFactory.Options();
int requiredWidth = ECameraConfig.getEntryById(Preferences.I_CAMERA_IMAGE_RESOLUTION.get()).getyAxe();
float bitmapWidth = optionsIn.outWidth;
int scale = Math.round(bitmapWidth / requiredWidth);
optionsOut.inSampleSize = scale;
optionsOut.inPurgeable = true;//avoiding memory leaks
return BitmapFactory.decodeFile(filePath, optionsOut);
And I belive you dont need 2 InputStream.
So here is my problem. I've got an image view containing a large bitmap (meaning that the imageview only shows a part of it since the bitmap is larger than the image view). I want to be able to center the bitmap in the imageview at the coordinates x, y (x and y are coordinate of the bitmap).
Any idea how I could achieve it ?
Regards,
Rob
try with
ImageView.ScaleType FIT_CENTER
see here the documentation http://developer.android.com/reference/android/widget/ImageView.ScaleType.html
now depending on how large is exactly your bitmap you might want to reduce it's size by using something like this
private Bitmap decodeFile(File f) {
Bitmap b = null;
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(f);
BitmapFactory.decodeStream(fis, null, o);
fis.close();
int scale = 1;
if (o.outHeight > 1024 || o.outWidth > 900) {
scale = (int) Math.pow(2, (int) Math.round(Math.log(1024 / (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();
} catch (IOException e) {
}
return b;
}
credits to How to scale bitmap to screen size?