Android blank bitmap - android

I'm creating bitmaps using inSampleSize. I've come across an unusual problem. On some images the rescaled image is blank.
Example 1:
Original: 4288x2848
inSampleSize = 2;
Rescale: 2144x1424
Result is a blank image
Original: 4288x2848
inSampleSize = 4;
Rescale: 1072x712
Result is a proper image
Example 2:
Original: 4752x3168
inSampleSize = 4;
Rescale: 1188x792
Result is a proper image
This one fails at inSampleSize = 2 also.
Now none of the failures throw the obvious OOM error. However, it certainly seems as I approach 3MB image sizes the BitmapFactory.decodeFile(..) is silently failing to create the image.
I can tighten the memory constraint, but I'd really like to know what is causing this artifact; appreciate any insight. Thanks!

Related

Resize an image on Android

after googling a lot I have not yet found a way to resize an image preserving quality.
I have my image - stored by camera in full resolution - in
String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/my_directory/my_file_name.jpg";
Now, I need to resize it preserving aspect ratio and then save to another path.
What's the best way to do this without occurring the error "Out of memory on a xxxxxxx-byte allocation."?
I continue to retrieve this error on Samsung devices, I tried in every way, even with the library Picasso.
Thanks!
1st things 1st: depending on device and bitmap size, no matter what magic code you do, it will crash! Specially cheap Samsung phones that usually have no more than 16mb of RAM to the VM.
You can use this code How to get current memory usage in android? to check on amount of memory available and deal with it properly.
When doing those calculations, remember that bitmaps are uncompressed images, that means, even thou the JPG might be 100kb, the Bitmap might take several MB.
You'll use the code shown here https://developer.android.com/training/displaying-bitmaps/load-bitmap.html to read the bitmap boundaries, and do an approximate scale down as close as possible to the size you actually need, or enough to make the device not crash. That's why it's important to properly measure the memory.
That 1st code takes virtually no RAM as it creates from the disk, making it smaller by simply skipping pixels from the image. That's why it's approximate, it only does in power of 2 the scaling.
Then you'll use the standard API to scale down to the size you actually need https://developer.android.com/reference/android/graphics/Bitmap.html#createScaledBitmap(android.graphics.Bitmap, int, int, boolean)
so the pseudo code for it, will be:
try{
Info info = getImageInfo(File);
int power2scale = calculateScale(info, w, h);
Bitmap smaller = preScaleFromDisk(File, power2scale);
Bitmap bitmap = Bitmap.createScaledBitmap(smaller, w, h, f);
} catch(OutOfMemoryError ooe){
// call GC
// sleep to let GC run
// try again with higher power2scale
}

How to solve java.lang.OutOfMemoryError trouble in 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

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.

Android Live Wallpaper Animation

What's the best way to display an animation as a live wallpaper? Right now I have a gif split into 11 pngs (one per frame) and then I just am doing
public Bitmap frame0;
ArrayList<Bitmap> frameArray = new ArrayList<Bitmap>();
frame0 = BitmapFactory.decodeResource(getResources(), R.drawable.nyancat0);
frame0 = Bitmap.createScaledBitmap(frame0, minWidth, minHeight, true);
frameArray.add(frame0);
Then I just use a For Loop to loop through the frames and draw them on a canvas
canvas.drawBitmap(frameArray.get(indexnumber), 0, 0, mPaint);
and then I just change my indexnumber++ unless it's 11, then I go back to 1.
That works, but of course, storing that many Bitmaps is very memory inefficient. This stops me from doing multiple layers or other cool effects without lagging and battery drain. Is there a better way to display an animation on the Android Live wallpaper? I tried Movie for displaying the whole GIF but that's not supported for live wallpapers.
How long does the loading of images take? If it's negligible then why not load each image in right before you display it, discarding the old one? That way you only have 1 image in memory at any one stage.
Alternatively do something akin to using a back buffer, have two spaces in memory, one for the image being displayed now, an another into which you're loading the next image. When it's time to change you make the newly loaded bitmap visible, unload the other and then load the next frame into that.
Despite what people say, you actually can have a lot of images in your Live Wallpaper. The only tricky thing is the memory limit. I had as much as 40 .pngs loaded in my application and i reloaded them once in a minute.
But when you handling that many images in your application, you have to load them in a smart way:
public BitmapResult decodeResource(int file, int scale){
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inPurgeable = true;
o.inInputShareable = true;
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, file, o);
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inPreferredConfig = Bitmap.Config.ARGB_8888;
o2.inSampleSize=scale;
return new BitmapResult(BitmapFactory.decodeResource(resources, file, o2),o2.outWidth,o2.outHeight);
}
You see that scale variable? It should be a power of 2 and it scales your bitmap down.
In case things got wrong, clean the bitmaps and reload bitmaps with a lower quality:
void init()
{
try
{
loadFirstBitmap();
loadSecondBitmap();
}
catch(java.lang.OutOfMemoryError error)
{
/*some infinite loop breaker*/
scale *= 2;
cleanup();
init();
}
}
Also, system won't get rid of a bitmaps for you, you have to clean them yourself and then probably call the garbage collector:
bitmap1.recycle();
bitmap2.recycle();
System.gc();
Resizing your bitmaps to the size you need is also a good idea because otherwise system would probably call createScaledBitmap each time you try to draw it which would require additional memory.
I never figured what's the memory cap for such kind of apps is and is it that a memory heap limit which most often equals 24 MB, but i can tell you that my app takes up to 13 MB of memory and no one ever reported a crash on Android devices >= 2.2.
So if you follow some optimization rules, you can load as much bitmaps in your application as you need.

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