This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Android: Strange out of memory issue while loading an image to a Bitmap object
OutOfMemoryError: bitmap size exceeds VM budget :- Android
I have an application where users can take pictures or use one already existing in the gallery, and attach to a news, after saving the news in the database (I am only saving the image path content :/ / ...), I display all news in a ListView with an image title and date. to display the images in the adapter I'm using the following:
...
image.setImageURI(null);
System.gc();
image.setImageURI(Uri.parse(noticia.getIMAGEM()));
...
however even using the System.gc (); each new image that is loaded'm getting
12-12 14:59:37.239: E / AndroidRuntime (4997):
java.lang.OutOfMemoryError: bitmap size exceeds performer VM budget
also already tried using
image.setImageURI(null);
System.gc();
in onResume(), onPause(), onDestroy(), but nothing worked.
also read this post: [link][1]
if(!((BitmapDrawable)image.getDrawable()).getBitmap().isRecycled()){
((BitmapDrawable)image.getDrawable()).getBitmap().recycle();
}
Bitmap thumbnail = null;
try {
thumbnail = MediaStore.Images.Media.getBitmap(context.getContentResolver(), Uri.parse(img));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
image.setImageBitmap(thumbnail);
but then I get the following error:
12-12 15:28:42.879: E/AndroidRuntime(5296):
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap
android.graphics.Bitmap#40756520
and besides when my list view to scroll up and down it keeps crashing, I believe that it is requesting the images ..
I do not know what else to do, any tips?
EDIT:
I found another post in the same solution to this problem worked perfectly, however I believe it would need a cache for the images because when scrolling the list up or down the one she fought, and this is very bad for the User Experience . Any idea to solve?
the following code:
in adapter:
...
String imagePath = getPath(Uri.parse(img));
image.setImageBitmap(decodeSampledBitmapFromResource(imagePath, 85, 85));
...
methods:
public String getPath(Uri uri)
{
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return cursor.getString(idx);
}
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 = 2;
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;
}
public static Bitmap decodeSampledBitmapFromResource(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);
}
It sounds like you want to ge thumbnail from local gallery. There is a better solution directly provided by the SDK :
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap thumbnail = Thumbnails.getThumbnail(context.getContentResolver(), /*ID of the local item*/, Thumbnails.MINI_KIND, options);
// Calculate inSampleSize
options.inSampleSize = UIUtils.calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
thumbnail = Thumbnails.getThumbnail(context.getContentResolver(), /*ID of the local item*/, Thumbnails.MINI_KIND, options);
Related
Steps my application performs:-
Download a large no of images and save them on the SDCard.
Load every image into Bitmap and resize them, after resizing replace this resized image with the original one.
My code:-
Bitmap myimage = loadimage(path+temppos+".jpg");
Bitmap finalimage = getResizedBitmap(myimage,width,height);
//save image
.....
//recyclebitmap
myimage.recycle();
finalimage.recycle();
loadimage:-
public Bitmap loadimage(String path)
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inDither=true;
options.inPurgeable=true;
options.inInputShareable=true;
return BitmapFactory.decodeFile(path, options);
}
Now I' populating these images on gridview.
Output(Before):-
Output (After):-
Where Before corresponds to initially when only a few images are downloaded.
And After corresponds to after all the images are downloaded.
Now, I think it is happening maybe because of Bitmap.recycle() method but don't know the reason. Please correct me if I am wrong and point out the error here.
Edit: I must add the grid view shows around 50 downloaded images, but only the first three images are becoming unrecognizable.
Thanks.
For getting your resized Bitmap you can use the below code: (taken from this tutorial)
public static Bitmap decodeSampledBitmapFromPath(String path, int reqWidth,
int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
return bmp;
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
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;
}
You can also take a look at the official site for getting a hint on loading Bitmaps
Edit
Try to change bitmap configuration to ARGB_8888 for best quality
because RGB_565 configuration can produce slight visual artifacts depending on the configuration of the source (taken from docs)
Update
I think you can take a look at this answer , I think this would solve your problem
I'm implementing ListView with Custom Adapter that extends BaseAdapter. In my app images are downloaded from URLs and then set as bitmap images in Listview.
The problem is that after complete downloading of 2 images java.Lang.OutOfMemoryError occurs and app crashes. I know this is memory issue but I don't know what steps should I take to avoid this... I worked with compressed images and they worked fine.
Below is my Code for getView() of CustomAdapter and doInBackground() of AsyncTask.
Thankx in advance...
Any suggestions will be appreciated..
public View getView(final int position, View convertView, final ViewGroup parent) {
if (convertView == null){
convertView = inflater.inflate(R.layout.row, parent, false);
}
img_name = "test" + position;
image = (ImageView) convertView.findViewById(R.id.imageView1);
Log.d("URL", ""+values[position]);
AsyncTaskRunner runner = new AsyncTaskRunner();
final int x = (int) getItemId(position);
runner.execute(values[x] , image , img_name);
return convertView;
}
doInBackground(Object... params)
protected ImageView doInBackground(Object... params) {
//publishProgress("Calculating..."); // Calls onProgressUpdate()
URL imageURL = null;
try {
url = (String) params[0];
img = (ImageView) params[1];
name = (String) params[2] + ".png";
imageURL = new URL(url);
Log.d("URL", ""+params[0]);
}
catch (MalformedURLException e) {
e.printStackTrace();
}
try {
HttpURLConnection connection= (HttpURLConnection)imageURL.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream inputStream = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
resized = Bitmap.createScaledBitmap(bitmap, 200, 200, true);
}
catch (IOException e) {
e.printStackTrace();
}
return img;
}
OnPostExecute(ImageView result)
protected void onPostExecute(ImageView result) {
result.setImageBitmap(resized);
}
java.Lang.OutOfMemoryError
Reason:
Each process in android is assigned a max heap size which varies from
device to device.(approx. 16 MB on avg.).And high resolution images when used in application occupies large space of this heap.So when a new instance of the bitmap is created and if total size exceeds the allocated heap size, this is error is thrown by the JVM.
Solution:
Android provide a way to handle this problem.Before decoding bitmap we just decode it with options.inJustDecodeBounds = true. where options is the instance of BitmapFactory. It does not load bitmap into memory but it help us to find the width and height of a bitmap so that we can reduce the height and width according to our device.
Then down-scale your bitmap to a create a smaller sized image which would in turn take up less space on the heap.
Here is the way to do it.
BitmapFactory.Options bmpBuffer = new BitmapFactory.Options();
bmpBuffer.inSampleSize = 3;
Bitmap bmp = BitmapFactory.decodeFile(path, bmpBuffer);
This would make your bitmaps 1/3 rd of the original size and hence
would take up 1/3rd space as well.
Eg.
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);
}
Method to Calculate sample 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) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
Note:
One additional thing you can do is use following ,in case there is no other option available. However I want to caution you that it would affect other applications on your device. Hence I would not recommend you to use this.
<application
android:largeHeap="true">
</application>
largeHeap="true" will allow the application to use more heap if it is available.However your app will spend more time during garbage collection.Other apps on the device might get kicked out of memory.
CommonsWare has explained it here
You can use Picasso Library to download images from a url efficiently besides having more options to resize image size, cache, etc. ..
use this bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
I am processing up to 1200 images. I optimized it to work from 100 images up to 500 with the help of previous questions found here. Now, this is what I have:
public Bitmap getBitmap(String filepath) {
boolean done = false;
int downsampleBy = 2;
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filepath, options);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565;
while (!done) {
options.inSampleSize = downsampleBy++;
try {
bitmap = BitmapFactory.decodeFile(filepath, options);
done = true;
} catch (OutOfMemoryError e) {
// Ignore. Try again.
}
}
return bitmap;
}
This function is called in a loop, and it goes really fast until it hits the 500th image. At this point it slows down, until it finally stops working at around the 600th image.
At this point I don't know how else to optimize it to make it work. What do you think is happening and how can I fix it?
EDIT
// Decode BItmap considering memory limitations
public Bitmap getBitmap(String filepath) {
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filepath, options);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
options.inSampleSize= calculateInSampleSize(options, 160, 120);
return bitmap = BitmapFactory.decodeFile(filepath, 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 / 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;
}
Made the changes of the accepted answer. Using the function from Google's tutorials to get the correct sample size. Added largeHeap in the manifest and only calling System.gc() once before I loop through all the images.
First of all, you should never expect to catch an Error. Described here: Java documentation An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.
There is some help about loading bitmaps: Android Developers | Loading large bitmaps
You can get some more memory by declaring the largeHeap="true" attribute in your Application Manifest.
And also the System.gc() call might help freeing some unused memory, but I won't really rely on that call.
I am implementing an application in which an activity shows a image view with full screen. Now i have some images downloaded in application's file directory. I want to set image to image view in that activity for which i am scaling down the larger images. I am getting OutOfMemory error after 10 to 15 mins of using application. error is in decodeFile of BitmapFactory. Here is my code :
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;
}
public static Bitmap decodeSampledBitmapFromFile(String imagePath,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imagePath, options);
}
Right now i am giving reqHeight = 500 and reqWidth = 500
This code is given in Android guidelines page. Is there anything i am missing, if so please help me out. I tried lots of things but not getting any solution.
Change you int inSampleSize = 1; to int inSampleSize = 2 or 3 and test the application again, I hope this will solve your problem. I also suggest insted of this technique,try lazy loading for all you image dowanload by doing so there will be no bitmap and outOfMemoryError. Yo can have lazy loading - https://github.com/thest1/LazyList
Try using this
Bitmap resizedbitmap2 = Bitmap.createScaledBitmap(your_bitmap, width, height,
true);
it will help you.
There might be a memory leak in your app if you are storing list of bitmaps returned by decodeSampledBitmapFromFile somewhere. Try to check for memory leak and also use Bitmap.recycle() to recycle bitmaps and help android free memory.
I have a problem when I take a photo with the camera in some devices and versions of Android. For example, in my Xperia U v4.0.3 my code works good but in another xperia U v2.3.3 it doesn't work...
I read a lot of about this error but I couldn't fix it...
My code to take a photo and show it:
public void callCamera(){
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File photo = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photo));
imageUri = Uri.fromFile(photo);
startActivityForResult(intent, SELECT_CAMERA);
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SELECT_CAMERA) {
Uri selectedImage = imageUri;
getContentResolver().notifyChange(selectedImage, null);
ContentResolver cr = getContentResolver();
try {
imageSelected = android.provider.MediaStore.Images.Media
.getBitmap(cr, selectedImage);
// I tried to read the image created for the camera in the Sd
//But I've got the same error...
//imageSelected = Utils.readImageSD(this, selectedImage.getPath());
imageSelected = Utils.rotateImage(Utils.scaleBitmap(imageSelected, 160, 160));
ivImageLoad.setImageBitmap(imageSelected);
} catch (Exception e) {
Utils.showToast(getApplicationContext(), R.string.errorLoad);
Log.e("Camera", e.toString());
}
}
}
I hope somebody can help me... I don't know what can I do....
Thanks in advance.
Regards.
It looks like you only want to display a 160x160 bitmap, but on this line you are reading in the whole bitmap and then later scaling it down.
imageSelected = android.provider.MediaStore.Images.Media(cr, selectedImage);
You can avoid ever loading the whole bitmap into memory by using inJustDecodeBounds, and then you can scale it using inSampleSize.
Here is some example code from the Android series on Loading Large Bitmaps Efficiently
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;
}
public static Bitmap decodeSampledBitmapFromUri(Uri uri,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
}
Then to get your 160x160 image you would just do:
ivImageLoad.setImageBitmap(decodeSampledBitmapFromUri(selectedImage, 100, 100));
Maybe the origin of the problem is the default Heap size. There are ways to increase it:
For Android 2.2 or lower versions:
dalvik.system.VMRuntime.getRuntime().setMinimumHeapSize(sizeInBytes);
For Android 2.3 or or higher versions:
android:largeHeap="true"
Check if the source of the problem is this, increasing the heap accordingly.