I want to load images with big size, but i have problem to make it. it Crashes, because OutOfMemoryError. here is my array :
Integer[] img = {
R.drawble.pic1, R.drawble.pic2, R.drawble.pic3, R.drawble.pic4
};
how to convert img[0],img[1],img[2] and img[3] to bitmap?
Please help.. Sorry my bad english.
Try this code, you'll convert an array of ids into an array of Bitmap:
Integer[] img = { R.drawble.pic1, R.drawble.pic2, R.drawble.pic3,
R.drawble.pic4 };
Bitmap[] bitmap = new Bitmap[img.length];
for (int i = 0; i < img.length; i++)
bitmap[i] = BitmapFactory.decodeResource(getResources(), img[i]);
If you have problems to convert big size image take a look at Displaying Bitmaps Efficiently.
Check here: http://developer.android.com/training/building-graphics.html
and the class BitmapFactory: http://developer.android.com/reference/android/graphics/BitmapFactory.html
Android apps have a limited heap size (minimum on old devices is as little as 16MB). This varies per device, so be careful during QA. Since a bitmap can take as much as 4 bytes per pixel, large bitmaps can easily fill up your heap (1000 x 1000 pixels = 3.8MB)
What this means is that you have to be very careful and use several tactics to avoid wasting memory:
Don't load all the bitmaps at the same time to memory. If you're showing them for example in a ViewPager you can load only 1-2 pages at a time. When a page is hidden, destroy its bitmap and call bitmap.recycle() to make sure the memory is freed immediately.
If the bitmap is larger than your screen size, you can scale it down to save memory. The technique here will let you load it initially reduced by 2/3/4.. If you need more fine-tuning, you can load it full size and then rescale using Bitmap.createScaledBitmap. Just don't forget to recycle() the original large bitmap immediately.
Make sure to explore your app's memory usage by using memory inspection tools such as DDMS or Eclipse MAT. I recommend to do most explorations on Android 4+ since 2.x saved bitmaps in the native heap which can't be explored easily. If you can keep your app under 16MB peak memory consumption, you should never hit an OutOfMemoryError again.
Related
I would like to know if there are any optimizations that could be used to improve the speed when using a large quantity of bitmaps drawn on a screen.
I use a canvas which I load all my resources at the initialization and use createBitmap when I need to update the bitmap. I use ~10-15kb files with my Galaxy Note 3 and notice a lag (xxhdpi) when I reach around 20 bitmaps which gets nearly unusable around 35+.
I am using createbitmap constantly because the bitmaps use frame animation and matrix to rotate.
So far the only thing i've tried that i've noticed a difference is inBitmap which gives about 5-10% increase in the GC_FOR_ALLOC freed.
Anyone care to chime in on a good answer for what is better? I've heard flash AIR is a good choice to go with using cacheAsBitmapMatrix, but I would like a different option (just personal pref).
EDIT:
(rectf bounds = bitmap bounds)
matrix.setRotate(rotation, rectf.centerX(), rectf.centerY());
ship1 = Bitmap.createBitmap(ship1, 0, 0, ship1.getWidth(), ship1.getHeight(), matrix, true);
I think I understand my problem, I should be calling
canvas.drawBitmap(ship1, matrix, paint);
But in my onDraw method I am using
canvas.drawBitmap(ship1, srcRectf, dstRectf, paint); //srcRectf = null
I use dstRectf to move my bitmap around, but I suppose this can be replaced with setTranslate. I'll try it out, thanks Mehmet!
Bitmap stores the pixel data in the native heap*, not in the java heap, and takes care of managing it by itself. That would mean GC shouldn't give you any serious headaches.
The problem is probably constantly using createBitmap(), which is usually a really costly operation. This will make a disk IO call at worst, or a relatively big memory allocation at best. You would like to use it as little as possible, i.e. only when initially reading them from the disk.
Instead I advise you to use a Matrix in conjunction with a Canvas. Just change your Matrix and with each step repaint your Bitmaps with it.
EDIT:
*Only correct for Android 2.3.3 <-> Android 3.0
I am trying to run a sketch that is supposed to show images (png´s, between 100kb and 1,5mb in size, 55.4mb total) in a coverflow animation. it works with about 10 images, but using more I get a out of memory error. I am loading the images file names into an string array like so:
String[] names = {"00.jpg", "01.jpg", "02.jpg"};
and then they get loaded into the sketch like so:
covers = new Cover[names.length];
for (int i = 0; i < covers.length; i++ ) {
covers[i] = new Cover(names[i]);
}
initCovers();
covers class:
class Cover {
PImage img;
Cover( String name ) {
img = loadImage(name);
public void drawCover() {
beginShape();
textureMode(NORMALIZED);
texture(img);
vertex(-300, -300, 0, 0, 0);
vertex( 300, -300, 0, 1, 0);
vertex( 300, 300, 0, 1, 1);
vertex(-300, 300, 0, 0, 1);
endShape();
when I run the sketch, my ram (8gb) gets filled within seconds, and the sketch doesn´t even load, it just crashes. when I start the sketch with about 10 images, everything works fine ( bout 1,5gb of ram usage).
my question is: why is it using so much memory? is it normal? is there a way to make it run more memory efficient (e.g. freeup memory of images that are not currently displayed because we can only see about 3 images at once on screen).
EDIT: I think the problem is that in the cover class, each time it gets called a new PImage is created. could that be possible?
image size in memory: width * height * (color depth/8), so for my images (1575y1969, 24bit) that woul be 8,9mb. times 91 images: about 807mb of memory usage just for the images.
Now that I understand the use-case better, I recommend to change the entire approach.
The types of applications (e.g. seen above) that you are trying to emulate do not load the entire slew of images as soon as the app. is presented. Instead, they read the small group of images that they are going to present to the user first. As the user approaches the end of that group, the software flushes some of the images at the start of the sequence (if memory is tight) and loads some more at the end.
Try increasing the JVM heap space
java -Xmx1024m
(Yes, I know, 1gig is a 'little' excessive, but after some experimentation, this value can be trimmed down)
As #millimoose states, the images loaded by Java are uncompressed into memory when they are loaded, so even a small image of 100kb on disk can suddenly occupy mb's of RAM when uncompressed. It becomes even more complicated when you start dealing with the alpha channel as well.
The size of compressed images isn't a good guide to the memory requirements.
The size in pixels is better. For example a modern camera photo with 8 megapixel resolution requires at least 32mb of memory to represent. If you are manipulating images this size with swing, double or triple that, at least. It's easy to gobble up a lot of memory.
Also, Java's internal memory management isn't very good at dealing with chunks this size.
I would say try painting them to a component using a single temp Cover as a handle as opposed to having N-many objects hanging around? And if you need interactions with images after draw time just hold back some simple meta data about their draw positions and dimensions, then use click event x,y etc to look up a a given image to allow you to work with it.
I want to load an image on android
background = BitmapFactory.decodeResource(getResources(),R.drawable.hangmanbegin);
background = Bitmap.createScaledBitmap(background,screenx,screeny,false);
The image is 800*1280 pixels , so if I'm correct it should use arround 3MB of memory space?
But my heap grows from 15MB to 29MB just at that phase , so no window or context leaking?
How is this explained? en what can you do about it?
Thnx in advance!
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.
from http://developer.android.com/training/displaying-bitmaps/index.html
credit and below it a way to approach a fix https://stackoverflow.com/a/10127787/643500
I'm trying to load a background image for a game as well as some smaller images, placing them on a Canvas, and letting people interact with the smaller overlayed images (like move, rotate)
In order to maintain aspect ratio (e.g. 5:3) I tried loading in the images as a bitmap and resizing them myself. The idea was to do cropping/letter-boxing for the background according to the canvas's width/height, and maintain the correct ratio of size for the smaller images.
Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), resourceImg);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg2, 0, 0, width, height, matrix, true);
In order to cater for tablets/phones i have a background PNG background image at 1600x1000 and 200kb.
However I am now struggling with out of memory issues due to the bitmap being 1600x1000x4byte=6.4 mb of ram and more when it tries to resize.
(I am using the emulator at the moment when these issues occur)
I decided to change it to use canvas.setBackgroundResource
SceneCanvas sceneCanvas = (SceneCanvas) findViewById(R.id.the_canvas);
sceneCanvas.setBackgroundResource(R.drawable.my_scene_1600x900);
This works well, except it fills the screen and does not maintain aspect ratio.
Is there a way to set the background maintaining aspect ratio? Or have I just gone down the wrong route completely and should use ImageViews and render to the canvas somehow to avoid OutOfMemory issues
Given that Java code is only allowed a heap size of around 20MB or so, you’re always going to have trouble with large bitmaps (unless you resort to native code in C/C++/etc).
One option is to use a BitmapFactory to load your image, and in the Options you can specify an inSampleSize to downsample the image as it’s being read in. This should avoid chewing up memory by trying to load the entire original image. There is even an inJustDecodeBounds option, so your code can check the dimensions of the image, instead of having them hard-wired into the code.
It seems that the memory limit on Android is somewhere between 16 - 24 MB memory (depending on device). This is regardless of whether the device has a lot more memory. Also, the memory used by Bitmaps is included in the limit, resulting in lang.OutOfMemoryError: bitmap size exceeds VM budget. After some searching, there are 3 options I could find:
Allocate memory from native code using the NDK (native development kit) and JNI
For images one can also use OpenGL textures, the texture memory is not counted towards the limit.
take advantage of certain bitmap options to reduce size; http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html
To see how much memory your app has allocated one can use, android.os.Debug.getNativeHeapAllocatedSize().
I realize that there are many similar questions, but most involve scaling down the bitmap, or explicit calls to bitmap.recycle() and System.gc() which doesn't guarantee anything (and in my case failed to prevent the error).
Edit: I have also tried using isPurgable = true when creating my bitmap.
Edit: Also, I have only tested this with Froyo (2.2.2) on Motorola Droid.
Here is the scenario: I am loading one bitmap (width: 1500, height: 2400). This takes up roughly 14 MB. The rest of the app is minuscule with regard to memory consumption (easily less than 2 MB).
I am using the bitmap with a transformation matrix to pan and zoom around on a surface view. On first load, this works perfectly. However, when I exit the app and relaunch it, I get the dreaded OutOfMemoryError. On third launch it works, on fourth it crashes ... and so on.
I don't need to save state, and so I tried calling finish() in onPause() (as well as the recycle() and gc() methods mentioned above). Finish() seems to stop all threads, but does not clear the memory.
I should mention that I am also using a technique which I found in a comment from this question.
Also Please check this
So, my image is loaded from the web, as an immutable bitmap. Its bytes are then saved to sdcard (very slow) just to be reloaded back to a mutable bitmap. If jumping through all these hoops is laughable, please educate me...
For my case, clearing all memory allocated for the app would be acceptable (if it doesn't generate crash messages). Is there anyway to just totally clear the memory allocated to my app so that each restart is as clean as the first launch?
Is there any solution involving tiling? Surely I am missing something.. since the image file itself (a png) is only a few kilobytes, and I have viewed larger images in the stock gallery app without this problem.
Edit: I have determined the cause of the problem based on insight gleaned from #Jason Lebrun's answer. It turns out that the canvas I had used to draw on this bitmap held a reference too it, so that canvas needed to be set to null for it to be properly garbage collected. Hope this helps someone with a similar issue.
Are you experiencing this problem on Gingberbread, or a different version? Gingerbread has a lot of problems with apps that use a lot of Bitmap memory, so knowing the OS version can help with determining the nature of the problem.
In your case, it's hard to say exactly what might be the cause. However, with a 14MB bitmap, even a 2nd instance is likely to use up your available heap and cause a crash. On Gingerbread, it's pretty easy to end up with Bitmap memory sticking around for longer than it should, due to the concurrent GC + the fact that Bitmap memory is allocated in a native array.
When you exit the app, it's probably not being unloaded from memory. That would explain your crash pattern: the first time you run the app, the large bitmap is loaded. The 2nd time you launch it, it's actually just restarting an Activity for a process already in memory, and for some reason, the memory for the Bitmap is still hanging around taking up room. So the app crashes, and a new process is started, with a fresh heap. If recycling is not helping, you might still have a reference to the previous Bitmap sticking around. If you're always using the same Bitmap, reusing a static reference to it might help, although I'm not sure.
Are you sure that the Bitmap is not leaked via a leaked context, long running background code, or something similar?
Other answers here have good advice, which is to try tiling the Bitmap after you fetch it, and only loading tiles as necessary. If you don't need to support older versions, you can use BitmapRegionDecoder (http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html) to do this, but only on devices that support API level 10 or higher.
To expand on #jtietema's answer, have you tried loading/rendering only the part of your bitmap that would be visible after applying your transformation matrix? You could do this by using a bounds-only bitmap for the whole image and transforming that, and using the resulting rectangle as an input to your acquiring-bitmap-from-sd-card.
You can use something like the following to decrease the sample size. I use this method in one of my apps to display images from the assets directory. But you can play around with the sample size, I've used values of 1 for images that aren't to big (94kb) and 4 for larger images (1.9mb)
protected void loadImageIntoBitmap( String imageAssetFile, Bitmap bitmap, int sampleSize, ImageView imgvw ) throws IOException {
InputStream is = getContext().getAssets().open( imageAssetFile );
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
bitmap = BitmapFactory.decodeStream( is,null,options );
imgvw.setImageBitmap( bitmap );
is.close();
}
What are you doing with the bitmap? The resolution is way higher than that of your android device, so if you are viewing the whole bitmap than scaling it down would do.
If you are zooming in you could create just a subset of the bitmap and just load the part that is visible.
well whenever you are saving the image to SD card you can use a lower quality of the image like:
myBitmap.compress(Bitmap.CompressFormat.PNG, 85, fileOutputStream);
and/or also use the bitmapFactory options to get a smaller image to save memory:
BitmapFactory.Options factoryOptions= new BitmapFactory.Options();
factoryOptions.inSampleSize = samplesize;
note that calling recicle() and gc() doesn't mean that the resources will be freed immediatly.
As the docs say:
from myBitmap.recycle():
Free the native object associated with this bitmap, and clear the reference to the pixel data. This will not free the pixel data synchronously; it simply allows it to be garbage collected if there are no other references. The bitmap is marked as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing. This operation cannot be reversed, so it should only be called if you are sure there are no further uses for the bitmap. This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
and from System.gc():
Indicates to the VM that it would be a good time to run the garbage
collector. Note that this is a hint only. There is no guarantee that
the garbage collector will actually be run.
therefore your app might be still using the resources and then whenever you re open the app the system is still using the resources plus the new ones that you are generating, and that might be the reason of why you are getting the out of memory error.
So in short handling a large image in memory is not a good idea.
This method takes in the image path and gives you a drawable without crashing
For best possible quality of image,always call this method with argument imageSizeDivide's value =1
public Drawable recurseCompressAndGetImage(String image_path,
int imageSizeDivide) {
try {
Log.w("", "imageSizeDivide = " + imageSizeDivide);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = imageSizeDivide;// controls the quality of image
// Bitmap
Bitmap srcBmp = BitmapFactory
.decodeFile(image_path.trim(), options);
//next if-else block converts the image into a squire image.Remove this block if u wish
if (srcBmp.getWidth() >= srcBmp.getHeight()) {
dstBmp = Bitmap.createBitmap(srcBmp, srcBmp.getWidth() / 2
- srcBmp.getHeight() / 2, 0, srcBmp.getHeight(),
srcBmp.getHeight());
} else {
dstBmp = Bitmap.createBitmap(srcBmp, 0, srcBmp.getHeight() / 2
- srcBmp.getWidth() / 2, srcBmp.getWidth(),
srcBmp.getWidth());
}
dstBmp = Bitmap.createScaledBitmap(dstBmp, 400, 400, true);
return new BitmapDrawable(mContext.getResources(), dstBmp);
} catch (OutOfMemoryError e) {
//reduce quality and try again
return recurseCompressAndGetImage(image_path, imageSizeDivide * 2);
}
}