How to solve java.lang.OutOfMemoryError trouble in Android - android

Altough I have very small size image in drawable folder, I am getting this error from users. And I am not using any bitmap function in code. At least intentionally :)
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:683)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:513)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:889)
at android.content.res.Resources.loadDrawable(Resources.java:3436)
at android.content.res.Resources.getDrawable(Resources.java:1909)
at android.view.View.setBackgroundResource(View.java:16251)
at com.autkusoytas.bilbakalim.SoruEkrani.cevapSecimi(SoruEkrani.java:666)
at com.autkusoytas.bilbakalim.SoruEkrani$9$1.run(SoruEkrani.java:862)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5602)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)
According to this stackTrace I'm gettin this error at this line ('tv' is a textView):
tv.setBackgroundResource(R.drawable.yanlis);
What is the problem? If you need some other information about code, I can add it.
Thanks!

You can't increase the heap size dynamically but you can request to use more by using.
android:largeHeap="true"
in the manifest.xml,you can add in your manifest these lines it is working for some situations.
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="#style/AppTheme">
Whether your application's processes should be created with a large Dalvik heap. This applies to all processes created for the application. It only applies to the first application loaded into a process; if you're using a shared user ID to allow multiple applications to use a process, they all must use this option consistently or they will have unpredictable results.
Most apps should not need this and should instead focus on reducing their overall memory usage for improved performance. Enabling this also does not guarantee a fixed increase in available memory, because some devices are constrained by their total available memory.
To query the available memory size at runtime, use the methods getMemoryClass() or getLargeMemoryClass().
If still facing problem then this should also work
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
This is the optimal use of BitmapFactory.Options.inSampleSize with regards to speed of displaying the image.
The documentation mentions using values that are a power of 2, so I am working with 2, 4, 8, 16 etc.
Lets get more deeper to Image Sampling:
For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x128 pixel thumbnail in an ImageView.
To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize to true in your BitmapFactory.Options object. For example, an image with resolution 2100 x 1500 pixels that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image (assuming a bitmap configuration of ARGB_8888). Here’s a method to calculate a sample size value that is a power of two based on a target width and height:
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;
}
Note: A power of two value is calculated because the decoder uses a
final value by rounding down to the nearest power of two, as per the
inSampleSize documentation.
To use this method, first decode with inJustDecodeBounds set to true, pass the options through and then decode again using the new inSampleSize value and inJustDecodeBounds set to false:
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));
You can follow a similar process to decode bitmaps from other sources, by substituting the appropriate BitmapFactory.decode* method as needed.
I found this code also interesting:
private Bitmap getBitmap(String path) {
Uri uri = getImageUri(path);
InputStream in = null;
try {
final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
in = mContentResolver.openInputStream(uri);
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, o);
in.close();
int scale = 1;
while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) >
IMAGE_MAX_SIZE) {
scale++;
}
Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth + ",
orig-height: " + o.outHeight);
Bitmap bitmap = null;
in = mContentResolver.openInputStream(uri);
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
o = new BitmapFactory.Options();
o.inSampleSize = scale;
bitmap = BitmapFactory.decodeStream(in, null, o);
// resize to desired dimensions
int height = bitmap.getHeight();
int width = bitmap.getWidth();
Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
height: " + height);
double y = Math.sqrt(IMAGE_MAX_SIZE
/ (((double) width) / height));
double x = (y / height) * width;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) x,
(int) y, true);
bitmap.recycle();
bitmap = scaledBitmap;
System.gc();
} else {
bitmap = BitmapFactory.decodeStream(in);
}
in.close();
Log.d(TAG, "bitmap size - width: " +bitmap.getWidth() + ", height: " +
bitmap.getHeight());
return bitmap;
} catch (IOException e) {
Log.e(TAG, e.getMessage(),e);
return null;
}
How to Manage Your App's Memory: link
It's not a good idea to use android:largeHeap="true" here's the extract from google that explains it,
However, the ability to request a large heap is intended only for a
small set of apps that can justify the need to consume more RAM (such
as a large photo editing app). Never request a large heap simply
because you've run out of memory and you need a quick fix—you should
use it only when you know exactly where all your memory is being
allocated and why it must be retained. Yet, even when you're confident
your app can justify the large heap, you should avoid requesting it to
whatever extent possible. Using the extra memory will increasingly be
to the detriment of the overall user experience because garbage
collection will take longer and system performance may be slower when
task switching or performing other common operations.
After working excrutiatingly with out of memory errors i would say adding this to the manifest to avoid the oom issue is not a sin
Verifying App Behavior on the Android Runtime (ART)
The Android runtime (ART) is the default runtime for devices running Android 5.0 (API level 21) and higher. This runtime offers a number of features that improve performance and smoothness of the Android platform and apps. You can find more information about ART's new features in Introducing ART.
However, some techniques that work on Dalvik do not work on ART. This document lets you know about things to watch for when migrating an existing app to be compatible with ART. Most apps should just work when running with ART.
Addressing Garbage Collection (GC) Issues
Under Dalvik, apps frequently find it useful to explicitly call System.gc() to prompt garbage collection (GC). This should be far less necessary with ART, particularly if you're invoking garbage collection to prevent GC_FOR_ALLOC-type occurrences or to reduce fragmentation. You can verify which runtime is in use by calling System.getProperty("java.vm.version"). If ART is in use, the property's value is "2.0.0" or higher.
Furthermore, a compacting garbage collector is under development in the Android Open-Source Project (AOSP) to improve memory management. Because of this, you should avoid using techniques that are incompatible with compacting GC (such as saving pointers to object instance data). This is particularly important for apps that make use of the Java Native Interface (JNI). For more information, see Preventing JNI Issues.
Preventing JNI Issues
ART's JNI is somewhat stricter than Dalvik's. It is an especially good idea to use CheckJNI mode to catch common problems. If your app makes use of C/C++ code, you should review the following article:
Also, you can use native memory (NDK & JNI), so you actually bypass the heap size limitation.
Here are some posts made about it:
How to cache bitmaps into native memory
https://stackoverflow.com/a/9428660/1761003
JNI bitmap operations , for helping to avoid OOM when using large images
and here's a library made for it:
https://github.com/AndroidDeveloperLB/AndroidJniBitmapOperations

I see only two options:
You have memory leaks in your application.
Devices do not have enough memory when running your application.

If you are getting this Error java.lang.OutOfMemoryError this is the most common problem occurs in Android. This error is thrown by the Java Virtual Machine (JVM) when an object cannot be allocated due to lack of memory space.
Try this android:hardwareAccelerated="false" , android:largeHeap="true"in your
manifest.xml file under application like this:
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme"
android:hardwareAccelerated="false"
android:largeHeap="true" />

You should implement an LRU cache manager when dealing with bitmap
http://developer.android.com/reference/android/util/LruCache.html
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
When should I recycle a bitmap using LRUCache?
OR
Use a tier library like Universal Image Loader :
https://github.com/nostra13/Android-Universal-Image-Loader
EDIT :
Now when dealing with images and most of the time with bitmap I use Glide which let you configure a Glide Module and a LRUCache
https://github.com/bumptech/glide

Few hints to handle such error/exception for Android Apps:
Activities & Application have methods like:
onLowMemory
onTrimMemory
Handle these methods to watch on memory usage.
tag in Manifest can have attribute 'largeHeap' set to TRUE, which requests more heap for App sandbox.
Managing in-memory caching & disk caching:
Images and other data could have been cached in-memory while app running, (locally in activities/fragment and globally); should be managed or removed.
Use of WeakReference, SoftReference of Java instance creation , specifically to files.
If so many images, use proper library/data structure which can manage memory, use samling of images loaded, handle disk-caching.
Handle OutOfMemory exception
Follow best practices for coding
Leaking of memory (Don't hold everything with strong reference)
Minimize activity stack e.g. number of activities in stack (Don't hold everything on context/activty)
Context makes sense, those data/instances not required out of scope (activity and fragments), hold them into appropriate context instead global reference-holding.
Minimize the use of statics, many more singletons.
Take care of OS basic memory fundametals
Memory fragmentation issues
Involk GC.Collect() manually sometimes when you are sure that in-memory caching no more needed.

android:largeHeap="true" didn't fix the error
In my case, I got this error after I added an icon/image to Drawable folder by converting SVG to vector. Simply, go to the icon xml file and set small numbers for the width and height
android:width="24dp"
android:height="24dp"
android:viewportWidth="3033"
android:viewportHeight="3033"

Check the image size
I was loading a ~350kB image in an imageview directly via XML (app:srcCompat) which was resulting in OOM error and the application crashed.
To solve it, I loaded the exact same image using Glide into the same imageview and it worked!
Lesson: Reduce image size / defer loading of image

Related

Android: Load large image from storage into ImageView

I'm doing a gallery image. I get path of image in device and parse to URI.
Then I use Picasso Android Lib to load image into Imageview in Gridview. It's work fine until have a large image. Picasso can not load large image. I got error Out Of Memory. Is there any suggestion to load large image into ImageView? And have any lib to load image into ImageView can instead Picasso?
I found ImageLoader lib for my problem. It works fine. I tested on my project, and then I saw that ImageLoader looks better than Picasso.
Learn how to use common techniques to process and load Bitmap objects in a way that keeps your user interface (UI) components responsive and avoids exceeding your application memory limit. If you're not careful, bitmaps can quickly consume your available memory budget leading to an application crash due to the dreaded exception:
java.lang.OutofMemoryError: bitmap size exceeds VM budget.
There are a number of reasons why loading bitmaps in your Android application is tricky:
Mobile devices typically have constrained system resources. Android devices can have as little as 16MB of memory available to a single application. The Android Compatibility Definition Document (CDD), Section 3.7. Virtual Machine Compatibility gives the required minimum application memory for various screen sizes and densities. Applications should be optimized to perform under this minimum memory limit. However, keep in mind many devices are configured with higher limits.
Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the camera on the Galaxy Nexus takes photos up to 2592x1936 pixels (5 megapixels). If the bitmap configuration used is ARGB_8888 (the default from the Android 2.3 onward) then loading this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the per-app limit on some devices.
Android app UI’s frequently require several bitmaps to be loaded at once. Components such as ListView, GridView and ViewPager commonly include multiple bitmaps on-screen at once with many more potentially off-screen ready to show at the flick of a finger.
Read More Regarding this issue
Here is an example I once used, but it is not perfect! (Sorry)
You can reduce the bitmap size:
public Bitmap resizeBitmap(Bitmap bitmap) {
if (bitmap.getHeight() > 4096 || bitmap.getWidth() > 4096) {
int width = (int) (bitmap.getWidth() * 0.9);
int height = (int) (bitmap.getHeight() * 0.9);
Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);
resizeBitmap(resizedBitmap);
returnresizedBitmap;
} else {
return bitmap;
}
}
If I should do it again: (not tested)
public Bitmap resizeBitmap(Bitmap bitmap) {
int originalWidth = bitmap.getWidth();
int originalHeight = bitmap.getHeight();
if (originalWidth > 4096 || originalHeight > 4096) {
int height;
int width;
if(originalHeight > originalWidth) {
height = 4096;
width = originalWidth / (originalHeight / 4096);
} else {
width = 4096;
height = originalHeight / (originalWidth / 4096);
}
Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);
return resizedBitmap;
} else {
return bitmap;
}
}
You can custom recent-images library for your purpose. It's very simple and easy to use library. It creates a thumbnail of image for showing in gridview and then in click opens the original.

Avoiding Out of Memory Error with Bitmap Factory, Even When Resizing Isn't an Issue?

I've researched at least 10 similar topics on SO, however, none have reached a definitive answer for me allowing me to avoid the Out of Memory error Bitmaps are known for.
Taking into consideration the advice from these previous questions, I constructed the following method setBipmapFromPath to produce an optimally sized (both in dimensions and kilobytes) wallpaper image from a file path. This method works fine on a large RAM device like my G2, however, it crashes in an emulator with 1.5GB of RAM using a 256kb picture.
I welcome any criticism that will help me prevent the Out of Memory error. My hope is to also ensure the image can still act as a proper background image, as in, fill the screen of the device reasonably without insane stretch marks.
My methods:
public void recycleWallpaperBitmap() {
if (mBitmap != null) {
mBitmap.recycle();
mBitmap = null;
}
}
private void setBitmapFromPath() {
// Recycle the bitmap just in case.
recycleWallpaperBitmap();
String path = mProfileManager.getWallpaperPath();
if (path != null) {
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int displayWidth = display.getWidth(); // deprecated
int displayHeight = display.getHeight(); // deprecated
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
mBitmap = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(path, options),
displayWidth, displayHeight);
}
}
This method works fine on a large RAM device like my G2, however, it crashes in an emulator with 1.5GB of RAM using a 256kb picture.
It will fail on the G2 as well, depending on where and when you call this method. Your "256kb" picture will take up several MB of heap space, and there is no assurance that you have that amount of heap space available in a single contiguous block.
Also, I would not use a class and method designed for creating thumbnails will be suitable for creating wallpaper-sized images.
I welcome any criticism that will help me prevent the Out of Memory error.
Use inSampleSize on your BitmapFactory.Options to downsample the image to closer to the right size while it is being read in.
Then, dump your use of ThumbnailUtils and allow your ImageView to scale it the rest of the way, to avoid making yet another copy of the image.
Bonus points for using inBitmap instead of junking and re-allocating your Bitmap every time, since the screen size is not changing, and therefore your wallpaper dimensions are not changing.
These techniques and more are covered in the developer documentation.

Exception : OutOfMemoryError

I have published my app in play store. Now In Crashes and ANRs I am getting following errors on 2 devices (Galaxy Note3 and Galaxy Note II). I dont know how to solve these errors and what type of error is this? So please help me to fix these errors. On other devices I am not getting any report there.
Errors-
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:677)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:507)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:872)
at android.content.res.Resources.loadDrawable(Resources.java:3022)
at android.content.res.Resources.getDrawable(Resources.java:1586)
at android.view.View.setBackgroundResource(View.java:16120)
at com.info.laughingbuddha.Buddha4.onCreateView(Buddha4.java:21)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:1500)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:472)
at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:163)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1068)
at android.support.v4.view.ViewPager.populate(ViewPager.java:914)
at android.support.v4.view.ViewPager$3.run(ViewPager.java:244)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803)
at android.view.Choreographer.doCallbacks(Choreographer.java:603)
at android.view.Choreographer.doFrame(Choreographer.java:572)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5293)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1259)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
at dalvik.system.NativeStart.main(Native Method)
Buddha4.java-
package com.info.laughingbuddha;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class Buddha4 extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.buddha, container, false);
TextViewEx t = (TextViewEx) rootView.findViewById(R.id.textView2);
t.setText("The Standing Happy Buddha brings riches and happiness.",true);
t.setMovementMethod(new ScrollingMovementMethod());
ImageView iv = (ImageView) rootView.findViewById(R.id.image1);
iv.setBackgroundResource(R.drawable.buddha4);
return rootView;
}
}
I dont know what code I need to post so If anyone require any code related to this please comment. Thanks.
Try to create scaled bitmap using following line of the code:
Bitmap scaledBitmap = Bitmap.createScaledBitmap(myBitmap, width,
height, true);
Check the following links:
android - out of memory exception when creating bitmap
Android out of memory exception with bitmaps
Can I catch out of memory exception in Android in Bitmap allocation for decoding a picture file?
If you have high resolution image , you should scale them down to avoid different devices to load the image without facing a memory problem.
In your case, some phones may not exhibit the same behavior on the first run, but eventually, without handling an optimized image loading solution, app will crash.
Check more on the "memory problem":
Topic under Load a Scaled Down Version into Memory.
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Out of memory error on Android
On how to avoid them:
How to avoid an out of memory error while using bitmaps in Android
For an overview:
http://blogs.innovationm.com/android-out-of-memory-error-causes-solution-and-best-practices/
http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html
Before setting a drawable as the background for your imageview, i.e.:
iv.setBackgroundResource(R.drawable.buddha4);
as #rup35h suggested in the answer, get a scaled bitmap or try other options like inSampleSize, do check how your solution affects the quality of your image too.
Sometimes when we use so many images & backgrounds in applications, it takes lot of space on android RAM.
This leads to force close your application by “Out of memory Bound Exception”.
It seems from your crash logs that you have drawable which is quite large, so to avoid the OutOfMemory you have keep drawables that would not take much memory. You can scale down the drawable either as some guys suggested in the answers.
If you don't want go through that and you have not much large size drawable then you can opt to increase your application heap size to large that would increase the heap memory.
How to increase heap size
You can use android:largeHeap="true" in application tag of Android manifest(Reference here) to request a larger heap size, but this will not work on any pre
Honeycomb devices.
On pre 2.3 devices, you can use the VMRuntime class, but this will not work on Gingerbread and above See below how to do it.
VMRuntime.getRuntime().setMinimumHeapSize(BIGGER_SIZE);
Before Setting HeapSize make sure that you have entered the appropriate size which will not affect other application or OS functionality.
Before settings just check how much size your app takes & then set the size just to fulfill your job. Dont use so much of memory otherwise other apps might affect.
Update
Note : It is always best practice to free the allocated memory of the bitmaps when these wont longer required, you can this bitmap.recycle()
One problem I've had a couple of times is having a lot of MDPI drawable resources but no HDPI / XHDPI versions. When loading these on an HDPI or XHDPI device, Android will scale them up by loading the original, then making a a scaled up copy in memory, meaning you've got two copies in memory at once, the original and a scaled up version. If the drawable is being stretched by the renderer (e.g. If it's a background for a view), then there's no need for Android to scale it up when it's loaded, as the renderer does that anyway. So if you only have one MDPI copy of the image and don't want Android to scale it up, you should put it in the drawable-nodpi folder instead of the drawable or drawable-mdpi folders.
EDIT:
You can use the render time scaling available on an image view by setting the android:scaleType attribute. E.g. you could set the image view's width and height to match_parent, then set the scale type to centerCrop or centerInside to have the image scale up when it's drawn while maintaining its aspect ratio. centerCrop will totally fill the image view but potentially cut off some of the image, whereas centerInside ensures that the whole image is visible, but may leave some blank space. The scaling will be done at render time, so won't use any extra memory.
It can all be done in the layout XML like so:
android:layout_height="match_parent"
android:layout_width="match_parent"
android:scaleType="centerCrop"
To have the image displayed fully inside the image view without cropping, change the last line to:
android:scaleType="centerInside"
Finally, if you don't want to maintain the aspect ratio, you can use the following scale type:
android:scaleType="fitXY"
You have to scale down your image resource when loading it.
If you are loading the image, using a Bitmap variable, you can use the inSample option, using BitmapFactory.Options. Don't use the Bitmap.createScaledBitmap as it will create another bitmap besides the original one!
Try the following code to load the bitmap:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //This will just get the size of your image
BitmapFactory.decodeFile(pictureFile.getAbsolutePath(), options);
//Get the downscale ratio
options.inJustDecodeBounds = false;
options.inSampleSize = calculateInSampleSize(options, widthOfTheImageView, heightOfTheImageView);
//Decode the bitmap using the downsampled image
Bitmap finalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image_name options);
//Finally, set the bitmap
imageView.setImageBitmap(finalBitmap);
Where widthOfTheImageView and heightOfTheImageView represent the size of your ImageView.
The calculateInSampleSize method (I got it from the Android developers tutorial):
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;
}
Do not load Bitmap directly if you are not sure about the size of the image. Sample it when the size is too large. At most time, the length of an image loaded should not exceed the length of your phone screen. Use code like this to sample image.
public static Bitmap loadImage(Context cx, Uri uri, int maxLength) {
InputStream is = cx.getContentResolver().openInputStream(uri);
Options opts = new Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, opts);
int length = Math.max(opts.outWidth, opts.outHeight);
int n = 1;
while (length > maxLength) {
maxLength /= 2;
n ++;
}
is = cx.getContentResolver().openInputStream(uri);
opts.inJustDecodeBounds = false;
opts.inPurgeable = true;
opts.inDither = true;
opts.inPurgeable = true;
opts.inSampleSize = sample;
Bitmap bm = BitmapFactory.decodeStream(is, null, opts);
return bm;
}
Remove all the views when you exit from an activity. Make it as null in OnDestroy().
if you are using Bitmaps
use
bitmap.recycle();
when you used it.
you may use
android:largeHeap="true"
in you manifest.xml to increase the heap size.

Bitmap recycle with largeHeap enabled

Before enabling largeHeap option, I was handling large bitmaps and it's consume almost the entire memory available for the application, and recycling it over navigation and loading new ones works round on almost the full heap available. However when some operations needs a bit more memory the application crashes. So I enabled largeHeap=true to have a bit more memory.
But doing this has a unexpected behavior, it's looks like that recycle() method of bitmaps do not work most of times, and the application that worked in 58Mb of memory (and exceeds sometimes throwing a OutOfMemoryException) now consumes memory exponentially and keeps growing (for now the test I did came to 231Mb allocated memory), the expected behavior is that the memory management keeps working and the application will not use more than 60Mb.
How can I avoid that? Or efficiently recycle bitmaps?
EDIT: Actually, I made it give a OutOfMemoryError when allocating more than 390Mb of memory on the device.
Reading GC_* logs shown that only GC_FOR_ALLOC that freed 3.8Mb sometimes, but almost never other GC runs freed something.
You should probably have a look at Displaying Bitmaps Efficiently which includes several ways to handle large Bitmaps Efficiently,
Loading Large Bitmaps Efficiently
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
This will give you the size of the image before downloading and on that basis you can check the size of your device and scale it using calculateInSampleSize() and decodeSampledBitmapFromResource() given in the explanation of docs.
Calculating how much we need to scale the image,
First way
if (imageHeight > reqHeight || imageWidth > reqWidth) {
if (imageWidth > imageHeight ) {
inSampleSize = Math.round((float)imageHeight / (float)reqHeight);
} else {
inSampleSize = Math.round((float)imageWidth / (float)reqWidth);
}
}
Second way
int inSampleSize = Math.min(imageWidth / reqWidth,imageHeight / reqHeight);
The you can set the inSampleSize,
options.inSampleSize = inSampleSize;
Then finally make sure you call,
options.inJustDecodeBounds = false;
else it will return Bitmap as null
Processing Bitmaps Off the UI Thread
Processing Bitmap on UI thread is never safe so its always better to do that in a background thread and update UI after the process is completed.
Caching Bitmaps
LruCache is available from API 12 but if you are interested it using below versions it is also available in Support Library too. So caching of Images should be done efficiently using that. Also you can use DiskLruCache for images where you want then to remain for longer period in extenal storage.
Clearing the Cache
Sometimes when your image size is too large even caching the image causes OutOfMemoryError so in that case its better to clear the cache when your image is out of the scope or not used for longer period so that other images can be cached.
I had created a demo example for the same, you can download from here
Your case behaves as expected. Before Honeycomb, recycle() was unconditionally freeing the memory. But on 3.0 and above, bitmaps are part of normal garbage collected memory. You have plenty of RAM on the device, you allowed the JVM to allocate more than the 58M limit, now the garbage collector is satisfied and has no incentive to reclaim memory occupied by your bitmaps.
You can verify this by running on an emulator with controlled amount of RAM, or load some memory consuming service on your device - GC will jump to work. You can use DDMS to further investigate your memory usage.
You can try some solutions for bitmap memory management: Bitmaps in Android Bitmap memory leaks http://blog.javia.org/how-to-work-around-androids-24-mb-memory-limit/, but start with the official Android bitmap tips, as explained in #Lalit Poptani's detailed answer.
Note that moving the bitmaps to OpenGL memory as textures has some performance implications (but perfect if you will render these bitmaps through OpenGL in the end). Both textures and malloc solutions require that you explicitly free the bitmap memory which you don't use anymore.
Definitely #Lalit Poptani answer is the way to do it, you should really scale your Bitmaps if they are very large. A preferable way is that this is done server-side if this is possible, since you will also reduce NetworkOperation time.
Regarding the implementation of a MemoryCache and DiskCache this again is the best way to do it, but I would still recommend to use an existing library, which does exactly that (Ignition) and you will save yourself a lot of time, and also a lot of memory leaks, since because your Heap does not get emptied after GC I can assume that you probably have some memory leaks too.
To address your dilemma, I believe this is the expected behaviour.
If you want to free up memory you can occasionally call System.gc(), but really you should for the most part let it manage the garbage collection itself.
What I recommend is that you keep a simple cache (url/filename to bitmap) of some sort which keeps track of its own memory usage by calculating the number of bytes that each Bitmap is taking up.
/**
* Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
* #param width
* #param height
* #param config
* #return
*/
public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
long pixels=width*height;
switch(config){
case ALPHA_8: // 1 byte per pixel
return pixels;
case ARGB_4444: // 2 bytes per pixel, but depreciated
return pixels*2;
case ARGB_8888: // 4 bytes per pixel
return pixels*4;
case RGB_565: // 2 bytes per pixel
return pixels*2;
default:
return pixels;
}
}
Then you query how much memory the app is using and how much is available, maybe take half of that and try to keep the total image cache size under that, by simply removing (dereferencing) the older images from your list when your are coming up against this limit, not recycling. Let the garbage collector clean up the bitmaps when they are both derefrrenced from your cache and are not being used by any views.
/**
* Calculates and adjusts the cache size based on amount of memory available and average file size
* #return
*/
synchronized private int calculateCacheSize(){
if(this.cachedBitmaps.size()>0){
long maxMemory = this.getMaxMemory(); // Total max VM memory minus runtime memory
long maxAllocation = (long) (ImageCache.MEMORY_FRACTION*maxMemory);
long avgSize = this.bitmapCacheAllocated / this.cachedBitmaps.size();
this.bitmapCacheSize = (int) (maxAllocation/avgSize);
}
return this.bitmapCacheSize;
}
I would recommend you stay away from using recycle(), it causes a lot of intermittent exceptions (like when seemingly finalized views try to access recycled bitmaps) and in general seems buggy.
You have to be very careful with handling bitmaps on Android. Let me rephrase that: you have to watch out handling bitmaps even on a system with 4 gigs of RAM. How large are these guys and do you have a lot? You might have to chop and tile it up if it's large. Remember that you use using video RAM, which is a different animal than system RAM.
Pre-Honeycomb, the Bitmaps were allocated on the C++ layer, so that RAM usage was invisible to Java and couldn't be accessed by the garbage collector. A 3 MP uncompressed Bitmap with the RGB24 colorspace uses around 9-10 megabytes (around 2048x1512). So, larger images can easily fill up your heap. Also remember that in whatever is being used for video RAM (sometimes dedicated RAM, sometimes shared with system), the data is usually stored uncompressed.
Basically, if you are targeting pre-Honeycomb devices, you almost have to manage Bitmap object as if you were coding a C++ program. Running the bitmap recycle() onDestory() usually works if there aren't many images, but if you have a ton of images on the screen, you may have to handle them on-the-fly. Also, if you launch another activity, you may have to consider putting in logic into onPause() and onResume().
You can also cache the images using the Android file system or SQLite when they aren't in video RAM. You may be able to get away with caching it in RAM if you are using a format like .jpg or a .png with a lot of repeated data/

Optimal use of BitmapFactory.Options.inSampleSize for speed

Thanks to Schermvlieger for asking this question on anddev.org,
I'm just copying his question to SO as nobody replied on the other site and I'm also facing the same problem.
I was wondering what would be the optimal use of BitmapFactory.Options.inSampleSize with regards to speed of displaying the image.
The documentation mentions using values that are a power of 2, so I am working with 2, 4, 8, 16 etc.
The things I am wondering about are:
Should I resample down to the smallest size that is still larger than the screen resolution, or should I sample down to the size just enough to avoid an OutOfMemoryError?
How would one calculate the maximum size of an image that could still be displayed without running out of memory? Does the color-depth of the image play a role as well, and the depth of the display?
Is it efficient to display images via two mechanisms (BitmapFactory for large files, setImageURI() for smaller ones) I am using an ImageSwitcher by the way.
Would it help creating the Bitmap, BitmapFactory.Options and inTempStorage in the beginning of the application or creating them only on the fly, when needed?
You should always try to load and pre-scale images so that they are as close as possible to their final displayed size. Scaling images at drawing time is extremely expensive and should be avoided at all cost.
Considering the memory cost of an image, yes, the color-deptch plays a very important role. Images in ALPHA_8 format use 1 byte per pixel, images in RGB_565 or ARGB_4444 use 2 bytes per pixel and images in ARGB_8888 use 4 bytes per pixel. The depth of the display does not matter at all. You should always try to use ARGB_8888 to get the best possible quality, but 565 can be good enough if your image is opaque.
You've asked good questions , but it all depends on your needs and how much memory you use.
I recommend checking out this link for many tips regarding bitmaps: http://developer.android.com/training/displaying-bitmaps/index.html .
In short , you should consider caching , downsampling , and using a good-enough bitmap format whenever you can.
Here's my answers to your questions:
Why not both? if you think there might be OOM , try to recycle old,unused bitmaps and then check again .
you can calculate the (estimated) size of the bitmap :
width*height*bytesPerPixel
where bytesPerPixel is usually 4 or 2 (depending on the bitmap format) .
Never used setImageURI , so I can't help you with that. I suggest downloading images in a background thread (using asyncTask is one way to do so) and showing them when it's ready.
If there are only a few that you know that won't take a lot of the memory , i guess it's ok. I still think caching could be better.
Here you can call the user defined method shrinkmehtod that actually send the string file path and the height and width to be reduce image to method.
Bitmap bit=shrinkmethod(arrpath1[position], 100, 100);
//iv.setImageURI(Uri.parse(arrpath1[position]));
iv.setImageBitmap(bit);
This is user defined method to reduce the size of image programmatically.
Bitmap shrinkmethod(String file,int width,int height){
BitmapFactory.Options bitopt=new BitmapFactory.Options();
bitopt.inJustDecodeBounds=true;
Bitmap bit=BitmapFactory.decodeFile(file, bitopt);
int h=(int) Math.ceil(bitopt.outHeight/(float)height);
int w=(int) Math.ceil(bitopt.outWidth/(float)width);
if(h>1 || w>1){
if(h>w){
bitopt.inSampleSize=h;
}else{
bitopt.inSampleSize=w;
}
}
bitopt.inJustDecodeBounds=false;
bit=BitmapFactory.decodeFile(file, bitopt);
return bit;
}
I hope this will help you to reduce size.
Hi try out calculating the inSampleSize using this logic
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight: Int = height / 2
val halfWidth: Int = 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
}

Categories

Resources