Trying to prevent OutOfMemoryError seeing MAT - Android - android

I am trying to prevent OutOfMemoryError in my android app. I have read many post but I cannot solve it yet.
The app has activities with background so I think this is the main problem. OutOfMemoryError only occurs in some devices (maybe due to VM heap) and I need to be sure that this error won't produce a crash in any device.
I have recently read about MAT (Memory Analytics plugin), and I have executed it during the app runtime, here you can see the result:
dominator_tree
report
In this activity I have a background for each orientation (home, home_land). Both sizes are the same (190kb, jpg). When I created the HPROF file the activity was in landscape orientation and I hadn't ran the portrait orientation before. What conclusion can I extract about this result in order to get my purpose?
I can add more information if it is necessary
EDIT
I tried to use the method of this page in order to avoid OutOfMemoryError too, but I couldn't get it. This was my code:
decodeFromResource class
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
public class decodeFromResource {
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;
}
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);
}
public static Drawable getDecodedDrawableFromResource(Resources res, int resId,
int reqWidth, int reqHeight){
return new BitmapDrawable(res, decodeSampledBitmapFromResource(res, resId, reqWidth, reqHeight));
}
}
onCreate method from the main activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
resources = getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
layoutHome = (LinearLayout) findViewById(R.id.home_layout);
if (resources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
layoutHome.setBackgroundDrawable(decodeFromResource
.getDecodedDrawableFromResource(resources, R.drawable.home,
metrics.widthPixels, metrics.heightPixels));
} else {
layoutHome.setBackgroundDrawable(decodeFromResource
.getDecodedDrawableFromResource(resources,
R.drawable.home_land, metrics.heightPixels,
metrics.widthPixels));
}
I had implemented the "Loading Large Bitmaps Efficiently" method only for the background, because apart from this I have only five small buttons with very small size. Should I also need to implement the method for them? Can you see any errors?

You are probably loading your jpg files as is, which can easily lead to OutOfMemory even on strong devices.
Keep in mind that an image loaded into memory with no compression and that on most devices a single pixel is represented by 4 memory bytes. A 7 MPixel image, for example, will require mem block of 28 MByte which may bring your app real close to OutOfMemory crash.
The solution is simple: always load a scaled-down image, according to your app's needs.
To do this start by reading your image size:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
The code above will NOT load the actual image file. it will only interrogate it
for its dimension.
Once you have the deminsion you can calculate the 'sample size' to be used for
loading the scaled image. A sample size of N will result in loading 1/(N*N) of the
orig image pixels, e.g. for sample size of 4 only 1/16 of the image pixels will be loaded.
And finally load the scaled down image:
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = mySampleSize; //<-----------------------
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeResource(res, resId, options);
Even when doing a scaled-down load it is a good idea to protect your code with
a try {...} catch(OutOfmemory) clause and allow for a graceful handling of load failure.
More details here: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Try using android:largeHeap="true" tag in your AndroidManifest.xml This should make sure that android will handle larger bitmaps.
EDIT
I have to point that this is not ultimate solution to your problem, it will not prevent OutOfMemory exception but they will be less likely to appear. Probably Gilad Haimov posted right solution to this problem

options.inPurgeable = true;
options.inInputShareable = true;
these flags allow the actual bits associated with a Bitmap object to be discarded under memory pressure and transparently re-decoded if it turns out you're still using the Bitmap.

Since bitmaps are decompressed, 190kb in storage doesn't really help, did you simply load up the bitmap with little regard of manipulating the parameters fitting memory paging requirement? About up to screen-resolution image can load straight up with no regard of memory.

I think when you used DecodeResource such as Bitmap ,you'd better use GC artificially,if you don't do that ,it maybe OOM.

Finally, after I have read many post, my last comment in #j__m answer was correct. The problem was drawable folders.
I found this: https://stackoverflow.com/a/19196749/2528167
"Option #1: Just ship the -xxhdpi drawables and let Android downsample them for you at runtime (downside: will only work on fairly recent devices, where -xxhdpi is known)."
I had all my pictures in a xxhdpi folder in order to let Android downsample them at runtime, but as CommonsWare said this only work on recent devices, so I have filled drawable-**dpi folders and now OutOfMemoryError doesn't appear.

Related

Bitmaps with Custom ListView Adapter

I am trying to load images into my listview from a custom adapter but the Bitmaps do not display the images until I scroll down and then scroll back up and I don't know why! At first when I had loaded the images , the application crashed as I wasn't loading the Images efficiently.
(I am storing images in a DB by encoded to base64. I know it's not a great thing to do but I need to just get things done as quick as possible).
Bonus question What is the best way to trigger an event to happen after TWO asynchronous DB calls are complete?
My code to load Bitmap:
public Bitmap decodeBitmap(String image, int reqWidth, int reqHeight)
{
byte[] decodedByte = Base64.decode(image, 0);
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(decodedByte, 0, decodedByte.length, options);
options.inSampleSize = calculateSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeByteArray(decodedByte, 0, decodedByte.length, options);
return bmp;
}
public int calculateSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
final int height = options.outHeight;
final int width = options.outWidth;
int size = 1;
if (height > reqHeight || width > reqWidth)
{
if (width > height)
{
size = Math.round((float) height / (float) reqHeight);
}
else
{
size = Math.round((float) width / (float) reqWidth);
}
}
return size;
}
The best and easiest way to load images into imageviews is to use a background process to accomplish the task, because blocking the interface will be bad user experience. The are a lot of code samples online, for instance you can use the ImageLoader class from this tutorial. Just add it to your project and use it like:
ImageLoader loader = new ImageLoader(context);
loader.DisplayImage(url, imagview);
EDIT:
When you fetch it from the web, you are most probably getting the image as a stream. You could do that, but the most efficient and popular way to deal with images in databases, is to not store the actual image data in the database, because read/write operation are not efficient.
Instead what you could do is, to store the stream you get from the web to a file in some folder, and once you store it, store the path pointing to it in the database where the image should be located. In this way your database is much lighter and you open the image just when you need it. Actually this is the correct way to deal with images because once your image size increase, reading from it won't be possible, since Android limits the cursor window size to 1MB.
So I advice you the modify your code as explained.

How to load large images in Android and avoiding the out of memory error?

I'm working on an app that uses large images (1390 × 870 : 150kb - 50kb). I'm adding images as I tap a trigger/ImageView.
At a certain point I'm getting an out of memory error:
java.lang.OutOfMemoryError
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:613)
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:378)
To resize the image I'm doing this:
Bitmap productIndex = null;
final String imageLoc = IMAGE_LOCATION;
InputStream imageStream;
try {
imageStream = new FileInputStream(imageLoc);
productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400);
productIV.setImageBitmap(productIndex);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, 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);
}
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 / 3;
final int halfWidth = width / 3;
// 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 got this way of resizing to save space from the Android Docs:
Loading Large Bitmaps Efficiently
According to the log this like is the culprit in the decodeSampledBitmapFromResource method :
return BitmapFactory.decodeFile(resId, options);
----- edit -----
Here is how I'm adding each item to the FrameLayout.
for(int ps=0;ps<productSplit.size();ps++){
//split each product by the equals sign
List<String> productItem = Arrays.asList(productSplit.get(ps).split("="));
String tempCarID = productItem.get(0);
tempCarID = tempCarID.replace(" ", "");
if(String.valueOf(carID).equals(tempCarID)){
ImageView productIV = new ImageView(Configurator.this);
LayoutParams productParams = new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
productIV.setId(Integer.parseInt(partIdsList.get(x)));
productIV.setLayoutParams(productParams);
final String imageLoc = productItem.get(2);
InputStream imageStream;
try {
imageStream = new FileInputStream(imageLoc);
productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400);
productIV.setImageBitmap(productIndex);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
productLayers.addView(productIV);
}
}
You can use another bitmap-config to heavily decrease the size of the images. The default is RGB-config ARGB8888 which means four 8-bit channels are used (red, green, blue, alhpa). Alpha is transparency of the bitmap. This occupy a lot of memory - imagesize X 4. So if the imagesize is 4 megapixel 16 megabytes will immidiately be allocated on the heap - quickly exhausting the memory.
Instead - use RGB_565 which to some extent deteriorate the quality - but to compensate this you can dither the images.
So - to your method decodeSampledBitmapFromResource - add the following snippets:
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
In your code:
public static Bitmap decodeSampledBitmapFromResource(Resources res, 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;
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
return BitmapFactory.decodeFile(resId, options);
}
References:
http://developer.android.com/reference/android/graphics/Bitmap.Config.html#ARGB_8888
High resolution devices such as S4 usually run out of memory if you do not have your image in the proper folder which is drawable-xxhdpi. You can also put your image into drawable-nodpi. The reason it would run out of memorey if your image just in drawable that the android would scale the image thinking that the image was designed for low resolution.
You can use this beautiful library https://github.com/davemorrissey/subsampling-scale-image-view
Here is how I'm adding each item to the FrameLayout
that's the problem, the code keep adding and adding more images, and doesn't matter how well you resize or how much memory the device have, at certain point it WILL run out of memory. That's because every image you add it's keeping in memory.
For this type of situation what the apps do is to use a ViewGroup that can recycle views. I don't know your layout, but usually is a ListView, GridView or a ViewPager, by recycling views you re-use the layout and can dispose re-load images as necessary.
For the specific purpose of loading and resizing images I strongly advise use Picasso library as it is VERY well written, simple to use and stable.
You are still going to need to manage the bitmap memory as I wouldn't try to allocate a total space more than 3x the size of the screen (if you think about it makes sense for scrolling behavior). If you are overlaying one image on top of another, at some point, you're hitting an Out Of Memory error. You may need to look at capturing the prior screen image as a single background image to make sure you still fit within the available memory. Or when a new image overlaps an existing image only load and render the visible portion. If performance becomes an issue, then you may need to consider OpenGL Textures but the memory allocation problem is still the same.
Do go through all of the Displaying Bitmaps Training as it should give you some additional ideas of how to handle display.
Use Fresco library to load large images will avoid this error.
in xml layout
<com.facebook.drawee.view.SimpleDraweeView
android:id="#+id/my_image_view"
android:layout_width="1300dp"
android:layout_height="1300dp"
fresco:placeholderImage="#drawable/my_drawable"
/>
and in javacode
Uri uri = Uri.parse("https://image.png");
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);

High resolution Image - OutOfMemoryError

I am developing an application for the Galaxy S4.
One of the requirements of the application is having a SplashScreen containing an Image of 1920x1080 pixels. Its a high quality .jpeg image and the size of the Image is about 2 Megabytes.
The problem is that I am getting an OutOfMemoryError as soon as I start the Application. I am quite stunned that this already happens with an image of just 2 megabyte in size? How can I fix this problem and display the image?
Changing the dimensions or the size of the image is not an option.
SplashScreen.java
public class Splashscreen extends Activity {
private static final int SPLASH_DURATION = 2000;
private boolean isBackPressed = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(LayoutParams.FLAG_FULLSCREEN, LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.splashscreen);
Handler h = new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
// check if the backbutton has been pressed within the splash_duration
if(!isBackPressed) {
Intent i = new Intent(Splashscreen.this, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Splashscreen.this.startActivity(i);
overridePendingTransition(R.anim.short_fade_in, R.anim.short_fade_out);
}
finish();
}
}, SPLASH_DURATION);
}
#Override
public void onBackPressed() {
isBackPressed = true;
super.onBackPressed();
}
}
And the splashscreen.xml
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ivSplashScreenImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/high_res_splashscreen_image"/>
ADDITIONAL INFORMATION:
Sometimes, (when a lot of device memory is available) the app is able to make it past the splash screen, but then, the memory consumption of the app is just insane. (around 100 megabyte). Even though I close the SplashScreen Activity and finish() it, it seems that there is a reference to the ImageView / the Image kept in memory.
How can I reduce the huge memory consumption?
When I do not display the splash screen, my app only consumes around 35MB
of memory. With the SplashScreen image, its around 100MB.
Three hints which should help you:
Use this to load your images, from Loading Large Bitmaps Android documentation:
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);
}
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;
}
Make sure you have only one instance of your Bitmap in memory. After displaying it, call recycle() and set your reference to null. You can use Memory Allocation Tracker to see what is allocated. You can also read HPROF files, as suggested in comments.
By default ARGB_8888 pixel format is used, which means 4 bytes per pixel. Very good article: Bitmap quality, banding and dithering. Your image is JPEG, so it doesn't have transparency, so you are wasting 1 byte on every pixel for alpha channel. It's not very probable, but maybe with acceptable quality you can use even more economical format. Take a look at them. Maybe RGB_565 for example. It takes 2 bytes for pixel, so your image would be 50% lighter. You can enable dithering to improve the quality of RGB_565.
I had a similar problem and the solution is simple. Put your high resolution 1920x1080 px image in the drawable-nodpi directory. The system does not scale resources tagged with this qualifier regardless of the current screen's density, which leads to the OutOfMemoryError, so the problem should disappear.
Please check Android documentation: http://developer.android.com/intl/es/guide/practices/screens_support.html
Hope it helps.

Android Bitmap OutOfMemoryError [duplicate]

This question already has answers here:
Android: BitmapFactory.decodeStream() out of memory with a 400KB file with 2MB free heap
(8 answers)
Closed 9 years ago.
I'm having an OutOfMemoryError in my VSD220 (It's a 22" Android based All in one)
for (ImageView img : listImages) {
System.gc();
Bitmap myBitmap = BitmapFactory.decodeFile(path);
img.setImageBitmap(myBitmap);
img.setOnClickListener(this);
}
I really don't know what to do, because this image is below the maximum resolution. The image size is something about (1000x1000), and the display it's 1920x1080.
Any help?
(That foreach cycle is for about 20 elements, it gots broken after 6, or 7 loops..)
Thanks a lot.
Ezequiel.
You should take a look at the training docs for Managing Bitmap Memory. Depending on your OS version, you could use different techniques to allow you to manage more Bitmaps, but you'll probably have to change your code anyway.
In particular, you're probably going to have to use an amended version of the code in "Load a Scaled Down Version into Memory", but I at least have found this section to be particularly useful:
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 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);
}
This method makes it easy to load a bitmap of arbitrarily large size
into an ImageView that displays a 100x100 pixel thumbnail, as shown in
the following example code:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Are you really sure you want to load the same Bitmap 20 times? Don't you want to load it once and set it inside the loop.
Still, loading a 1000x1000 pixel image is not guaranteed to work, regardless of screen resolution. Remember that a 1000x1000 pixel image takes up 1000x1000x4 bytes =~4MB (if you load it as ARGB_8888). If your heap memory is fragmented/too small you may not have enough space to load the bitmap. You may want to look into the BitmapFactory.Options class and experiment with inPreferredConfig and inSampleSize
I would suggest that you either use the suggestion by DigCamara and decide on a size and load a downsampled image of nearly that size (I say nearly because you won't get the exact size using that technique) or that you try to load the full size image and then recursively increase the sample size (by factors of two for best result) until you either reach a max sample size or the image is loaded:
/**
* Load a bitmap from a stream using a specific pixel configuration. If the image is too
* large (ie causes an OutOfMemoryError situation) the method will iteratively try to
* increase sample size up to a defined maximum sample size. The sample size will be doubled
* each try since this it is recommended that the sample size should be a factor of two
*/
public Bitmap getAsBitmap(InputStream in, BitmapFactory.Config config, int maxDownsampling) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
options.inPreferredConfig = config;
Bitmap bitmap = null;
// repeatedly try to the load the bitmap until successful or until max downsampling has been reached
while(bitmap == null && options.inSampleSize <= maxDownsampling) {
try {
bitmap = BitmapFactory.decodeStream(in, null, options);
if(bitmap == null) {
// not sure if there's a point in continuing, might be better to exit early
options.inSampleSize *= 2;
}
}
catch(Exception e) {
// exit early if we catch an exception, for instance an IOException
break;
}
catch(OutOfMemoryError error) {
// double the sample size, thus reducing the memory needed by 50%
options.inSampleSize *= 2;
}
}
return bitmap;
}

BitmapFactory.decodeStream issues

I am creating an application with frame by frame animation using almost 150 image frames. The animation is played by changing the background image of an Image view using Handler. Inside the handler I am retrieving the images from the folder /mnt/sdcard/Android/data/ApplicationPackage and changing it dynamically as the image view background by the following way :
FileInputStream in;
in = new FileInputStream(mFrames.get(imgpos));
bitmap = BitmapFactory.decodeStream(in);
if (in != null)
{
in.close();
}
This creates some issues in decoding the file input stream since it takes a lot of time for some images to create the bitmap. Image file size is almost less than 40 KB for each images, but it takes different duration to decode the images from the external directory for the same size files. I have tried to sample the file size and load but it directly affects the image clarity. Can any one please suggest me what is the better way to load the images to a bitmap from the external folder and with the same duration for all the images?
Thanks, Tim
I think you must preload the bitmap before displaying them... Use an array of bitmap, and a background task that load all the images when entering your activity.
Your handler is absolutely not allowed to perform time-consuming tasks such as loading bitmaps !
You can downscale the images before creating the bitmap for them.
public Bitmap decodeSampledBitmapFromPath(String path)
{
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, requiredWidth, requiredHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap newBmap = BitmapFactory.decodeFile(path, options);
return newBmap;
}
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
int inSampleSize = 1;
if (imageHeight > reqHeight || imageWidth > reqWidth)
{
if (imageWidth > imageHeight)
{
inSampleSize = Math.round((float)imageHeight / (float)reqHeight);
}
else
{
inSampleSize = Math.round((float)imageWidth / (float)reqWidth);
}
}
return inSampleSize;
}
If the images are a lot bigger than the view in which they are loaded, then this down scaling really helps create the bitmap faster. Also, the scaled Bitmap takes much less memory than directly creating the bitmap.
inJustDecodeBounds in BitmapFactory.Options when set to true lets you get the height and width before creating the Bitmap for it. So you can check if the images are larger than required. If yes, then scale them to the required height and width, and then create its Bitmap.
Hope this helps!

Categories

Resources