I have a hashmap of Bitmap , which provides the adapterview with required thmbnails of images to show.
My thumbnail size is likely within 64x64 dp box.
But still i see logs like heap grown for alloction of 1.xx mb. Then i printed the bytecounts, and verified that the code i use doesn't do actually thumbnailing. The resulting bitmaps are of size mostly greater than megbytes.
The code i use is as below
#Override
protected Bitmap doInBackground(Uri... params) {
data = params[0];
// Log.e(TAG,"Async Task Drawable doInBackground");
try {
Bitmap bmp;
if (MainActivity.thumbCache.containsKey(albumId)) {
bmp = (Bitmap) MainActivity.thumbCache.get(albumId);
Log.e("BMWT-HM-Size","getting from Cache"+String.valueOf(MainActivity.thumbCache.size()));
Log.e(TAG,": bmp.getByteCount() "+bmp.getByteCount());
} else {
in = res.openInputStream(data);
songArtOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in,null,songArtOptions);
Log.e(TAG,String.valueOf(songArtOptions.outWidth)+"x"+String.valueOf(songArtOptions.outHeight)+": "+songArtOptions.outMimeType);
songArtOptions.inSampleSize = calculateInSampleSize(songArtOptions,songArtWidth,songArtHeight);
Log.e(TAG,"subSampleLevel = "+String.valueOf(songArtOptions.inSampleSize));
in = res.openInputStream(data);
songArtOptions.inJustDecodeBounds = false;
bmp = BitmapFactory.decodeStream(in,null,songArtOptions);
MainActivity.thumbCache.put(albumId, bmp);
Log.e("BMWT-HM-Size","newly decoded"+String.valueOf(MainActivity.thumbCache.size()));
Log.e(TAG,": bmp.getByteCount() "+bmp.getByteCount());
}
return bmp;
} catch (FileNotFoundException e) {
return defaultArt;
}
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
int inSampleSize = 1;
if (options.outHeight > reqHeight || options.outWidth > reqWidth) {
int halfHeight = options.outHeight >> 1;
int halfWidth = options.outWidth >> 1;
// 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 (halfWidth > reqWidth && halfHeight > reqHeight) {
inSampleSize <<= 1;
halfWidth >>= 1;
halfHeight >>= 1;
}
}
return inSampleSize;
}
As a result, my heap grows as much images are put into the hashmap.
in OnTrimMemory I call, thumbCache.clear,which i hoped to release the occupied memory by the elements of hashmap, but it doesn't. The heap status stays the same.
How to clean this out. I want to maintain cache as long as the view is visible and want to clear the cache(which i mean releasing the occupied memory to be GC'ed) whne view is fully destroyed.
If you are sure the Bitmap has no further uses, you can call Bitmap.recycle() to reclaim memory after you remove it from your cache.
Related
im trying to adapt a method to prevent an outOfMemoryError when reading from an internet bitmap. Do I use it right? Isnt the stream read twice from internet?
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
URL url = new URL(imageUrl);
InputStream inputStream = url.openConnection().getInputStream();
BitmapFactory.decodeStream(inputStream, null, options);
// Calculate inSampleSize
int coverDimensions = CommonTasks.getDisplayMinSize(getActivity());
options.inSampleSize = calculateInSampleSize(options, coverDimensions, coverDimensions);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(inputStream, null, options);
private 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;
}
In the while use || to keep both width and height withing bounds. And no, you cannot reread an InputStream.
No. Theoretically you could use reset() but internet is sequential I/O:
boolen canReset = inputStream.markSupported();
if (canReset) {
inputStream.mark(Integer.MAX_VALUE);
}
... first read
if (canReset) {
inputStream.reset();
}
... second read
Instead of a boolean flag, catching an IOException on reset would be easier: it might be thrown anyway when the read limit is exceeded for instance.
Simply reread. The second time it might come from cache.
I want to store bitmap in to SD card but when I pick large bitmap I faced "out of memory error" problem.
My code:
FileOutputStream out = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
I read many likely questions but they answers not solve my problem because all of answers says you must save bitmap in low quality.
Pleas don't answer me to reduce quality because I need to store bitmap in high quality.
You can check the available memory size before loading bitmap, by
BitmapFactory.Options btmapOptions = new BitmapFactory.Options();
btmapOptions.inJustDecodeBounds = true;
then if the memory exceeds, you have to resize the bitmap, finaly set
BitmapFactory.Options btmapOptions = new BitmapFactory.Options();
btmapOptions.inJustDecodeBounds = false;
to load the bitmap.
For increasing allocated memory, you can use largeHeap set to true but which just increase the allocated memory,
Sample codeto resize bitmap,
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;
}
I am working on an android app, which has camera capture and photo uploading feature. If the device has a high resolution camera, the captured image size will be really large (1~3MB or more).
Since the app will need to upload this image to server, I will need to compress the image before uploading. If the camera captured a 1920x1080 full-res photo for example, the ideal output is to keep a 16:9 ratio of the image, compress it to be a 640x360 image to reduce some image quality and make it a smaller size in bytes.
Here is my code (referenced from google):
/**
* this class provide methods that can help compress the image size.
*
*/
public class ImageCompressHelper {
/**
* Calcuate how much to compress the image
* #param options
* #param reqWidth
* #param reqHeight
* #return
*/
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;
}
/**
* resize image to 480x800
* #param filePath
* #return
*/
public static Bitmap getSmallBitmap(String filePath) {
File file = new File(filePath);
long originalSize = file.length();
MyLogger.Verbose("Original image size is: " + originalSize + " bytes.");
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// Calculate inSampleSize based on a preset ratio
options.inSampleSize = calculateInSampleSize(options, 480, 800);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap compressedImage = BitmapFactory.decodeFile(filePath, options);
MyLogger.Verbose("Compressed image size is " + sizeOf(compressedImage) + " bytes");
return compressedImage;
}
The problem with the above code is:
It cannot keep the ratio, the code is forcing the image to resized to 480x800. if user captured a image in another ratio, the image will not look good after compress.
It doesn't functioning well. The code will always change the image size to 7990272byte no matter what the original file size is. If the original image size is pretty small already, it will make it big (my test result to take a picture of my wall, which is pretty much mono-colored):
Original image size is: 990092 bytes.
Compressed image size is 7990272 bytes
I am asking if there's suggestion of a better way to compress photo so it can be uploaded smoothly?
You need to decide on a limit for either your width or height (not both, obviously). Then replace those fixed image sizes with calculated ones, say:
int targetWidth = 640; // your arbitrary fixed limit
int targetHeight = (int) (originalHeight * targetWidth / (double) originalWidth); // casts to avoid truncating
(Add checks and calculation alternatives for landscape / portrait orientation, as needed.)
As #harism also commented: the large size you mentioned is the raw size of that 480x800 bitmap, not the file size, which should be a JPEG in your case. How are you going about saving that bitmap, BTW? Your code doesn't seem to contain the saving part.
See this question here for help on that, with the key being something like:
OutputStream imagefile = new FileOutputStream("/your/file/name.jpg");
// Write 'bitmap' to file using JPEG and 80% quality hint for JPEG:
bitmap.compress(CompressFormat.JPEG, 80, imagefile);
Firstly i check the size of image then i compress image according to size and get compressed bitmap then send that bitmap to server
For Compressed bitmap call below funtion we have to pass image path in below funtion
public Bitmap get_Picture_bitmap(String imagePath) {
long size_file = getFileSize(new File(imagePath));
size_file = (size_file) / 1000;// in Kb now
int ample_size = 1;
if (size_file <= 250) {
System.out.println("SSSSS1111= " + size_file);
ample_size = 2;
} else if (size_file > 251 && size_file < 1500) {
System.out.println("SSSSS2222= " + size_file);
ample_size = 4;
} else if (size_file >= 1500 && size_file < 3000) {
System.out.println("SSSSS3333= " + size_file);
ample_size = 8;
} else if (size_file >= 3000 && size_file <= 4500) {
System.out.println("SSSSS4444= " + size_file);
ample_size = 12;
} else if (size_file >= 4500) {
System.out.println("SSSSS4444= " + size_file);
ample_size = 16;
}
Bitmap bitmap = null;
BitmapFactory.Options bitoption = new BitmapFactory.Options();
bitoption.inSampleSize = ample_size;
Bitmap bitmapPhoto = BitmapFactory.decodeFile(imagePath, bitoption);
ExifInterface exif = null;
try {
exif = new ExifInterface(imagePath);
} catch (IOException e) {
// Auto-generated catch block
e.printStackTrace();
}
int orientation = exif
.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
Matrix matrix = new Matrix();
if ((orientation == 3)) {
matrix.postRotate(180);
bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
true);
} else if (orientation == 6) {
matrix.postRotate(90);
bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
true);
} else if (orientation == 8) {
matrix.postRotate(270);
bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
true);
} else {
matrix.postRotate(0);
bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
true);
}
return bitmap;
}
getFileSize funtion for getting the size of image
public long getFileSize(final File file) {
if (file == null || !file.exists())
return 0;
if (!file.isDirectory())
return file.length();
final List<File> dirs = new LinkedList<File>();
dirs.add(file);
long result = 0;
while (!dirs.isEmpty()) {
final File dir = dirs.remove(0);
if (!dir.exists())
continue;
final File[] listFiles = dir.listFiles();
if (listFiles == null || listFiles.length == 0)
continue;
for (final File child : listFiles) {
result += child.length();
if (child.isDirectory())
dirs.add(child);
}
}
return result;
}
I am downloading an image and i want to display it in a RelativeLayout as background. However since android has so man different screens, i am finding it difficult to resize the image according to screen size.
Here is my layout
[Top Bar]
[Relative Layout] ..width= fill_parent , height = wrap_content
[ListView]
In my resizing i am using this code
bitmap = Utilities.decodeSampledBitmap(in, imageView.getWidth(), imageView.getWidth());
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 decodeSampledBitmap(InputStream in, int reqWidth, int reqHeight)
{
if (in != null)
{
byte[] image;
try {
image = readFully(in);
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
BitmapFactory.decodeByteArray(image, 0, image.length, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(image, 0, image.length, options);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
else
return null;
}
However these are still stretching images and only in hdpi it seems something better, ldpi is worse, mdpi seems fine , xhdpi is bad too. Stetching is effecting. I can't fix the size of RelativeLayout or Use ImageView because It will then show space between Image and ListView..
What method should I adopt.
I suggest you not using the background image, but use ImageView and set his ScaleType to meet your needs.
I'm resizing an array of Bitmaps to a certain percentage of screen (So looks the same on all devices). Some of the bitmaps are sprites with + 256kb in size (explosions etc).
Obviously the VM is running out of memory once the bitmaps are converted twice, the bitmaps only convert at the beginning of the android application but it's still giving the error.
Can anyone tell me, is there a better, faster, more effcient way to return this peice of code as a bitmap.
Just out of curiosity are bitmap values passed by reference? (As in does the object parameter use the same line of memory for the same object?).
Anyhow's here is z code:
public Bitmap ResizeBitmap(Bitmap bitmap, float s_percentage, int frames, int viewport_width, int viewport_height)
{
float percentage = s_percentage / 100.0f;
float scale = viewport_width / 100 * percentage;
if(viewport_width < viewport_height)
{
scale = viewport_height / 100 * percentage;
}
int newWidth = (int) (bitmap.getWidth() * scale);
int newHeight = (int) (bitmap.getHeight() * scale);
if(newWidth <= 0 || newHeight <= 0)
{
// Extra check, for invalid width/height
Log.e("Function List, Resize Bitmap", "invalid dimension ("+newWidth+"x"+newHeight+")");
return bitmap;
}
//Round up to closet factor of total frames
int rW = (newWidth/frames)+1;
newWidth = rW*frames;
Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
return newBitmap;
}
To be in VM budget try to scale down your Bitmap like this.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile( filename, options );
options.inJustDecodeBounds = false;
options.inSampleSize = 4;
bitmap = BitmapFactory.decodeFile( filename, options );
if ( bitmap != null ) {
bitmap = Bitmap.createScaledBitmap( bitmap, width, height, false );
}
//Adjust SampleSize to values like 2, 4, 8 etc