In my app I put two Bitmaps. When the orientation is vertical, show the first image, if it's horizontal, show the second image. Using
Log.i("log","Total memory " + title + " = " + (int) (Runtime.getRuntime().totalMemory()/1024));
I found that after I change my phone's orientation a few times, the total memory grows. However, I would expect the memory to remain the same, it seems like the program is not properly freeing memory.
Here's my code
public Bitmap decodeSampledBitmapFromResource(int path, int reqWidth, int reqHeight, Context ctx){
// Читаем с inJustDecodeBounds=true для определения размеров
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap b = BitmapFactory.decodeResource(ctx.getResources(), path, options);
// Вычисляем inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Читаем с использованием inSampleSize коэффициента
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(ctx.getResources(), path, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth, int reqHeight) {
// Реальные размеры изображения
Log.i("log", "inSampleSize" + reqWidth);
Log.i("log", "inSampleSize" + reqHeight);
final int height = options.outHeight;
Log.i("log", "height" + height);
final int width = options.outWidth;
Log.i("log", "height" + width);
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Вычисляем наибольший inSampleSize, который будет кратным двум
// и оставит полученные размеры больше, чем требуемые
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
Log.i("log", "inSampleSize" + inSampleSize);
}
}
return inSampleSize;
}
private void readImage(int draw) {
//int px = getResources().getDimensionPixelSize(draw);
int pxW = displaymetrics.widthPixels;
int pxH = displaymetrics.heightPixels;
***if(bitmap != null){
bitmap.recycle();
bitmap = null;
}***
bitmap = decodeSampledBitmapFromResource(draw, My_px_W, My_px_H, this);
ivStart.setImageBitmap(bitmap);
}
Here's onCreate
if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
My_px_W = 200;
My_px_H = 150;
readImage(R.drawable.im2);
logMemory("'vertical'");
}
else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
My_px_W = 400;
My_px_H = 200;
readImage(R.drawable.s2);
logMemory("'horizontal'");
}
You need to call System.gc()after you called recycle if you want it to be immediately released.
Recycle on it's own is not enough as the garbage collector is not called.
Free the native object associated with this bitmap, and clear the
reference to the pixel data. This will not free the pixel data
synchronously; it simply allows it to be garbage collected if there
are no other references. The bitmap is marked as "dead", meaning it
will throw an exception if getPixels() or setPixels() is called, and
will draw nothing. This operation cannot be reversed, so it should
only be called if you are sure there are no further uses for the
bitmap. This is an advanced call, and normally need not be called,
since the normal GC process will free up this memory when there are no
more references to this bitmap.
You should call bitmap.recycle() and then call System.gc()
Be sure you aren't using the same bitmap after call recycle()
Related
I have a list adapter
adapter = new SimpleCardStackAdapter(this);
// System.gc(); i should call this function here or just before set adapter calling
int i=0;
if(cardModels.size() > VISIBLE_NUMBER_OF_CARDS){
i = cardModels.size()-VISIBLE_NUMBER_OF_CARDS;
}
for ( ;i < cardModels.size(); i++) {
//if(i<VISIBLE_NUMBER_OF_CARDS){
adapter.add(cardModels.get(i));
//}else{
// break;
//}
}
// System.gc(); i should call this function or here
mCardContainer.setAdapter(adapter);
my question is when adapter consume memory
during setadapter or during creating adapter
i m facing out of memory error ... adapter items has some large bitmaps
i have to call manually garbage collector before memory consuming operation will happen thanks.
You can use following function to get the bitmap in correspondence to the size of your imageview where you want to load them...this can help prevent creating large bitmaps for smaller imageviews even..
public static Bitmap decodeSampledBitmapFromResource(String filepath,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filepath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return 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;
}
i have made a demo for image editing,I am facing issue when i pass the image to another activity(Grow heap size),and my app crashed,Please tell me how cani sample and resize my bitmap directly (not from resources) to solve memory issue ,My code is as below:
code
if (Global.photo_editor_bitmap != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Global.photo_editor_bitmap.compress(Bitmap.CompressFormat.PNG,
100, stream);
byte[] byteArray = stream.toByteArray();
i = new Intent(PhotoEditor.this, Enhance.class);
i.putExtra("bitmap_image", byteArray);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
} else {
Toast.makeText(getApplicationContext(), "No Image", 1).show();
}
static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
int inSampleSize = 1; // Default subsampling size
// See if image raw height and width is bigger than that of required
// view
if (options.outHeight > reqHeight || options.outWidth > reqWidth) {
// bigger
final int halfHeight = options.outHeight / 2;
final int halfWidth = options.outWidth / 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;
}
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);
}
I got solution of decodebitmapFromresources,But my bitmap is available,SO how to use this direclty.
I have the same problem the app gets crashed and showing message in log for heap size, allocation size and bitmap size, what i have done is checked the device width & height and on the basis of that scaled the bitmap or calculate sample size.
int screenHeight = getResources().getDisplayMetrics().heightPixels;
int screenWidth = getResources().getDisplayMetrics().widthPixels;
get the width and height and then put it in reqWidth and reqHeight
Im a new in android im studying the image bitmap,
please add some info if something im missing..
My Learnings!
1. In decoding a bitmap from a file which is already an image,
String pathName = Environment.getExternalStorageDirectory()
+ "/download/" + "ZG4T5vzQSPQ.png";
imgView.setImageBitmap(decodeSampledBitmap(pathName, 100, 120));
public static Bitmap decodeSampledBitmap(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.decodeFile(pathName, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
android.util.Log.d("calculateInSampleSize()", "H:" + options.outHeight
+ " W:" + options.outWidth);
// 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; // 1 = 1 * 2
}
}
android.util.Log.d("calculateInSampleSize()", "inSampleSize:"
+ inSampleSize);
return inSampleSize;
}
If you want to decode an image to bitmap a single path is needed but
if you want to decode a video and get its videoThumbnail why single path is not
enough? you need to create a videoThumbnail like this!
Bitmap bmp = ThumbnailUtils.createVideoThumbnail(pathName,
MediaStore.Images.Thumbnails.MINI_KIND);
I decode a small jpg image to bitmap in my app.
The image is only 40K.
I use these methods:
public Bitmap decodeSampledBitmapFromResource(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;
Bitmap bmp=BitmapFactory.decodeFile(pathName, options);
return bmp;
}
public 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;
}
and when the program goes to:
Bitmap bmp=BitmapFactory.decodeFile(pathName, options);
I get GC_Concurrent in the LogCat,
why?
GC_CONCURRENT per se is not bad. It's just a status message from the garbage collector doing its job. Image decoding involves memory allocations and it's normal to see garbage collector running.
My code code is:
public Bitmap loadPhoto(Uri uri) {
Bitmap scaled = null;
try {
scalled = Bitmap.createBitmap(
MediaStore.Images.Media.getBitmap(getContentResolver(), uri),
0,0,90, 90);
if (scaled == null) { return null; }
} catch(Exception e) { }
return scaled;
}
After this. I display scaled in ImageView. Every image comes from the device camera.
Every time, I get error: out of memory after I display three photos from camera. How to solve this?
Answer of Praveen Katha will always return null. Here is the updated answer.
Here is the trick, close the input stream after every use. Input Stream means to be used one time. For more information, please follow this answer
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) {
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;
}
public static Bitmap decodeSampledBitmapFromUri(Context context, Uri imageUri, int reqWidth, int reqHeight) throws FileNotFoundException {
Bitmap bitmap = null;
try {
// Get input stream of the image
final BitmapFactory.Options options = new BitmapFactory.Options();
InputStream iStream = context.getContentResolver().openInputStream(imageUri);
// First decode with inJustDecodeBounds=true to check dimensions
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(iStream, null, options);
if (iStream != null) {
iStream.close();
}
iStream = context.getContentResolver().openInputStream(imageUri);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(iStream, null, options);
if (iStream != null) {
iStream.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
Try using BitmapFactory to fix the problem http://developer.android.com/reference/android/graphics/BitmapFactory.html
The MediaStore.getBitmap method is a convenience method that does not specify a sample size when obtaining the bitmap. If you are using getBitmap(ContentResolver, Uri), and want to use a sample size, just use the ContentResolver to get the input stream, and decode the bitmap as you would normally (calculating sample size first, and then loading it with the appropriate sample size).
For those who are looking for code sample:
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) {
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;
}
public static Bitmap decodeSampledBitmapFromUri(Context context, Uri imageUri, int reqWidth, int reqHeight) throws FileNotFoundException {
// Get input stream of the image
final BitmapFactory.Options options = new BitmapFactory.Options();
InputStream iStream = context.getContentResolver().openInputStream(imageUri);
// First decode with inJustDecodeBounds=true to check dimensions
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(iStream, null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(iStream, null, options);
}