I am currently working on a project in which one I would like to rotate a bitmap.
The first time, I create my bitmap with the following code :
myBitmap = BitmapFactory.decodeResource(getResources(), drawableResource);
Then I rotate the bitmap using the following code :
final Matrix matrix = new Matrix();
matrix.postRotate(currentRotate);
myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, directionBitmap.getWidth(), directionBitmap.getHeight(), matrix, true);
I works, but after several times, the memory increases and I have the following exception :
java.lang.OutOfMemoryError: Failed to allocate a 119071756 byte
allocation with 16775968 free bytes and 96MB until OOM
It seems that the old bitmaps are still in memory. How to delete/recycle them in order to save the memory ?
Thank your for your help.
I can suggest you to use Glide library from Github.
This library works in background threads. Anyway you can perform your rotation on Background like this:
runOnUiThread(new Runnable(final Matrix matrix = new Matrix();
matrix.postRotate(currentRotate);
myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, directionBitmap.getWidth(), directionBitmap.getHeight(), matrix, true);
)
In my case, the problem was with the size of the bitmaps I was using. The bitmaps I was using were of very high pixels for the given device. In such case, Android system has to descale them to lower pixel density i.e. the one that will suit your device. When you rotate the bitmap, Android system takes up a lot of memory to descale it to lower pixels. Also, Android gets busy and in some cases results in UI thread blocking.
Also, increase the heap size in your manifest file.
Related
I am using
Drawable drawable = res.getDrawable(id);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(bitmap);
bitmap.eraseColor(0);
drawable.setBounds(0,0, width, height);
drawable.draw(canvas);
return load(bitmap, linear);
to load a drawable from a resource id into OpenGL with a given width, and height. (Using
android.opengl.GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
)
The load function does the GL-calls, and calls also bitmap.recycle().
I specify width and height myself, because Android would match the resolution to the screen size, which I don't want.
Now my problem (this part is all working fine):
if I start my app for the first time, from Android Studio, everything works; HOWEVER if I want to restart it, it crashes because of OutOfMemoryError. I am doing the exactly same calls in both cases.
I located the issue to be in the resource management of Android, as you can see in the heap analysis:
my most expensive allocations
My images are way smaller than 9 MB each in raw (512x512, RGBA, so 1 MB).
How can I prevent Android from storing these large byte arrays, which probably are meant as some kind of cache; which however doesn't run on first start after app installation?
I am testing on Android 6.0.1, API Version 23, Galaxy S5.
Implementation of texImage2D looks like this:
public static void texImage2D(int target, int level, int internalformat,
Bitmap bitmap, int border) {
if (bitmap == null) {
throw new NullPointerException("texImage2D can't be used with a null Bitmap");
}
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
if (native_texImage2D(target, level, internalformat, bitmap, -1, border)!=0) {
throw new IllegalArgumentException("invalid Bitmap format");
}
}
It doesn't look like it's recycling anything. Are you sure you are not loading a huge bitmap into memory? Two calls of those are more than enough to guarantee a huge explosion in your app, if not just one (I've seen it happen many times in my app). Remember, restarting your activity does not mean restarting your proccess.
Run the Android Profiler before the first load and check how much memory it takes.
Also, you can cache and reuse bitmaps yourself.
I solved it (myself) by putting the files into the raw folder of the resource directory, and loading them using
fun loadBitmap(res: Resources, rawId: Int): Bitmap {
val inputStream = BufferedInputStream(res.openRawResource(rawId))
return BitmapFactory.decodeStream(inputStream)
}
and then calling
load(bitmap, linear);
and
bitmap.recycle()
like before.
Luckily those all were png/jpeg files, so I didn't need the additional features of the drawables folder. Using this, they'll automatically use their right resolution.
My Java RAM allocation is now back on 25 MB to 35 MB instead of the 110 MB when using the old way :).
Hi all I am developing live wallpapers, and I am using lot of bitmaps. I have tested my new live wallpaper for aboute a week, and it was up and runing perfectly, but as soon as I have uploaded it to a market I keep getting this kind of exceptions : java.lang.OutOfMemoryError for both android.graphics.Bitmap.nativeCreate and android.graphics.BitmapFactory.nativeDecodeAsset. I use this kind of lifecycle of an bitmap:
I create a reference like:
Bitmap dark = null;
Bitmap cave = null;
at onCreateEngine I init them like :
cave = BitmapFactory.decodeResource(getResources(), R.drawable.cave);
dark = BitmapFactory.decodeResource(getResources(), R.drawable.dark);
here is where it trows the exception. for these images: . and after all I draw them to an canvas like this:
canvas.save();
canvas.drawBitmap(bg, new Matrix(), new Paint());
canvas.drawBitmap(dark, new Matrix(), new Paint());
canvas.restore();
What Should I do? It is better to load the dark image just one picture and draw it to the canvas width*height times? Or Are there any methods to do? I think recycle or calling onDestroy. But I do not know when to call them because the exceptions are thrown at onCreate.Are the images too big? And why it is working smoothly on my device, and on the other devices it is throwing exceptions? The bitmaps are 1484*1484 dimension big and the clouds are 250*172 dimensional big, should they be in 2^x * 2^x dimension?
Just try to use Memory Optimizer and see where are you creating Memory Leaks. You can use Eclipse Memory Analyzer(MAT) for this. Its a very common problem with using bitmaps. By using BitMaps you need to be extra careful for memory leaks.
My app main purpose is to display images in following fashion as shown in image
private void setSelectedImage(int selectedImagePosition)
{
BitmapDrawable bd = (BitmapDrawable) drawables.get(selectedImagePosition);
Bitmap b = Bitmap.createScaledBitmap(bd.getBitmap(), (int) (bd.getIntrinsicHeight() * 0.9), (int) (bd.getIntrinsicWidth() * 0.7), false);
selectedImageView.setImageBitmap(b);
selectedImageView.setScaleType(ScaleType.FIT_XY);
}
Detailed code can be find here
exception is thrown at following line
Bitmap b = Bitmap.createScaledBitmap(bd.getBitmap(), (int) (bd.getIntrinsicHeight() * 0.9), (int) (bd.getIntrinsicWidth() * 0.7), false);
Above function is called from onItemSelected. **The app still works well on 2.2 and 2.3, but throws exception immediately on 4.1 Above code works fine, but throws following exception. I didnot see any crashes in 2.2 and 2.3, but it immedidately crashes in 4.1 Is there any major difference of memory management in Jelly beans? **:
java.lang.OutOfMemoryError
AndroidRuntime(2616): at android.graphics.Bitmap.nativeCreate(Native Method)
AndroidRuntime(2616): at android.graphics.Bitmap.createBitmap(Bitmap.java:640)
AndroidRuntime(2616): at android.graphics.Bitmap.createBitmap(Bitmap.java:586)
AndroidRuntime(2616): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:466)
AndroidRuntime(2616): at com.rdx.gallery.GalleryDemoActivity.setSelectedImage(GalleryDemoActivity.java:183)
It's important to note that following code can cause Exception:
Bitmap bitmap = Bitmap.createScaledBitmap(oldBitmap, newWidth, newHeight, true);
oldBitmap.recycle();
Proper is:
Bitmap bitmap = Bitmap.createScaledBitmap(oldBitmap, newWidth, newHeight, true);
if (oldBitmap!= bitmap){
oldBitmap.recycle();
}
because documentation says:
If the specified width and height are the same as the current width
and height of the source btimap, the source bitmap is returned and now
new bitmap is created.
http://www.youtube.com/watch?v=_CruQY55HOk. After andorid 3.0 bitmaps pixel data are stored on the heap. It seems you are exceeding heap memory size. Just because your app requires large heap do not use large heap. More the size of heap, more regular garbage collections. The video has a good explanation on the topic.
Also recycle bitmaps when not in use. Garbage collections on heap is done my mark and sweep , so when you recycle bitmaps it free's memory. So your heap size will not grow and run out of memory.
bitmap.recycle();
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html. Documentation on loading bitmaps efficiently. Have a look at loading scaled down version in memory.
Apart form this you can use Universal Image Loader. https://github.com/nostra13/Android-Universal-Image-Loader.
https://github.com/thest1/LazyList. Lazy Loading of Images.
Both use caching.
You are trying to access more memory then you have. Try to use
BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inDither=false;
opts.inSampleSize = 8;
opts.inPurgeable=true;
opts.inInputShareable=true;
opts.inTempStorage=new byte[16 * 1024];
Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.h1)
, 65,65, true),
Also look at below links to increase memory
http://developer.android.com/reference/android/R.styleable.html#AndroidManifestApplication_largeHeap
Detect application heap size in Android
EDIT 1
try to use nostras image downloader, you can use it to show image in local storage. And it manages memory very well ...
https://github.com/nostra13/Android-Universal-Image-Loader
I have this function that loads a big bitmap from SD and rotates it.
Yet after the second or third rotating I get a bitmap size exceeds VM budget error.
Any ideas why? I do recycle the old bitmap, don't it?
public void next(String s, int d)
{
if ( mBitmap!=null ) { mBitmap.recycle(); }
deg = deg + d;
mBitmap = BitmapFactory.decodeFile(s);
Matrix matrix = new Matrix();
matrix.postRotate(deg);
mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth() , mBitmap.getHeight(), matrix, true);
Thanks!
Its not uncommon for outof memory errors when you dont use bitmaps properly.
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.
There is a great android guide on
how to use Bitmaps efficiently.
Following the guide, you should be able to reduce your memory consumption dramatically with out losing any visible quality, avoiding unecessary crashes.
code seems fine.
however , this exception also depends on the size of the bitmap (resolution and bitmap format) , and other memory consuming objects.
I am streaming a video in android and I decode frames in native code and then copy the pixels to a bitmap, then display the bitmap in Java using canvas.unlockandpost with a while loop for all the bitmaps.
Everything is fine, but the streaming of bitmaps is very slow and causes a crash. I only see a message on logcat saying that "low memory no more background processes".
I see on the allocation table from eclipse, that the bitmaps that I created are not getting deleted from memory, even though, I am overwritng the pixels everytime. Is there any way I can clean up the memory it is keeping.
My code is as follows.
C Code :
AndroidBitmapInfo info;
void* pixels;
int ret;
if ((ret =AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
}
memcpy(pixels, pictureRGB, 480*320);
AndroidBitmap_unlockPixels(env, bitmap);
Java Code
Bitmap mBitmap = Bitmap.createBitmap(480, 320, Bitmap.Config.RGB_565);
renderbitmap(mBitmap, 0);
canvas.drawBitmap(mBitmap, 0, 0, null);
The code shown in your question is missing some critical parts to fully understand your problem, but it sounds like you're creating a new bitmap for every frame. Since Android only allows for about 16MB of allocations for each Java VM, your app will get killed after about 52 frames. You can create a bitmap once and re-use it many times. To be more precise, you are creating a bitmap (Bitmap.CreateBitmap), but not destroying it (Bitmap.recycle). That would solve your memory leak, but still would not be the best way to handle it. Since the bitmap size doesn't change, create it once when your activity starts and re-use it throughout the life of your activity.