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;
}
Related
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 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.
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?
So i've got a Uri of an image the user chooses out of images off his SD card. And i'd like to display a thumbnail of that image, because obviously, the image could be huge and take up the whole screen. Anyone know how?
You can simply create thumbnail video and image using ThumnailUtil class of java
Bitmap resized = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(file.getPath()), width, height);
public static Bitmap createVideoThumbnail (String filePath, int kind)
Added in API level 8
Create a video thumbnail for a video. May return null if the video is corrupt or the format is not supported.
Parameters
filePath the path of video file
kind could be MINI_KIND or MICRO_KIND
For more Source code of Thumbnail Util class
Developer.android.com
This code will do the job:
Bitmap getPreview(URI uri) {
File image = new File(uri);
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(image.getPath(), bounds);
if ((bounds.outWidth == -1) || (bounds.outHeight == -1))
return null;
int originalSize = (bounds.outHeight > bounds.outWidth) ? bounds.outHeight
: bounds.outWidth;
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = originalSize / THUMBNAIL_SIZE;
return BitmapFactory.decodeFile(image.getPath(), opts);
}
You may want to calculate the nearest power of 2 to use for inSampleSize, because it's said to be faster.
I believe this code is fastest way to generate thumbnail from file on SD card:
public static Bitmap decodeFile(String file, int size) {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, o);
//Find the correct scale value. It should be the power of 2.
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = (int)Maths.pow(2, (double)(scale-1));
while (true) {
if (width_tmp / 2 < size || height_tmp / 2 < size) {
break;
}
width_tmp /= 2;
height_tmp /= 2;
scale++;
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeFile(file, o2);
}
A few trying I could not get the thumbnail path of image from SD.
i am resolved this problem getting an android image bitmap, before I create an image view in adapter for gridview (or where you need). So i call method imageView.setImageBitmap(someMethod(Context context, imageID))
Bitmap someMethod(Context context, long imageId){
Bitmap bitmap = Media.Images.Thumbnails.getThumbnail(context.getAplicationContext.getContentResolver(), imageid, MediaStore.Images.Thumbnails.MINI_KIND, null);
return bitmap;
}
You can get image ID from your SD using this guide (Get list of photo galleries on Android)
If you like HQ thumbnails, so use [RapidDecoder][1] library. It is simple as follow:
import rapid.decoder.BitmapDecoder;
...
Bitmap bitmap = BitmapDecoder.from(getResources(), R.drawable.image)
.scale(width, height)
.useBuiltInDecoder(true)
.decode();
Don't forget to use builtin decoder if you want to scale down less than 50% and a HQ result.
I tested it in API Level 8 :)
This package will let you access image URI to receive image size, large Bitmap data, sampling image to any smaller size for saving memory and maximize performance.
It uses InputStream and BitmapFactory:
public int[] getImageSize(Uri uri){
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream input = this.getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(input, null, options); input.close();
return new int[]{options.outWidth, options.outHeight};
}
catch (Exception e){}
return new int[]{0,0};
}
public Bitmap BitmapImage(Uri uri){return BitmapImage(uri,-1,-1);}
public Bitmap BitmapImage(Uri uri, int maxSize){return BitmapImage(uri,maxSize,maxSize);}
public Bitmap BitmapImage(Uri uri, int Wmax, int Hmax){
try {
InputStream input = this.getContentResolver().openInputStream(uri);
double ratio=1;
if ((Wmax>-1)&&(Hmax>-1)){
int[] wh=getImageSize(uri); double w=wh[0], h=wh[1];
if (w/Wmax>1){ratio=Wmax/w; if (h*ratio>Hmax){ratio=Hmax/h;}}
else if (h/Hmax>1){ratio=Hmax/h;}
}
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = (int)Math.ceil(1/ratio);
bitmapOptions.inPreferredConfig=Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
input.close();
return bitmap;
}
catch (Exception e){}
return null;
}
Four functions for different use case:
/*
getImageSize(Uri uri): return int[]{ width, height}
BitmapImage(Uri uri): return Bitmap in full size
BitmapImage(Uri uri, int maxSize): return sampled Bitmap which is limited in square size
BitmapImage(Uri uri, int Wmax, int Hmax): return sampled Bitmap which is limited width and height
*/