My tablet app has to display a very large image (2500x6000) and allow the user to pan across, zoom in & out. Since it can't fit into memory I've broken it into tiles and am loading those as needed. The problem I'm running into is that whenever I unload or replace a bitmap I cause garbage collection which pauses my app with noticeable stutter. I was wondering if anyone had come up with any clever ways to work around this? I create my bitmaps using BitmapFactory.decodeResource. I've tried replacing the same bitmap but garbage collection still runs (assuming it dumps the old bitmap and replaces it with a new one).
Thanks!
Figured out the answer! In API11+ BitmapFactory.Options has an attribute called inBitmap which will reuse the bitmap when loading content. I've implemented it as such:
mBg[i] = Bitmap.createBitmap(800, 1232, Bitmap.Config.RGB_565);
mBgOptions[i] = new BitmapFactory.Options();
mBgOptions[i].inBitmap = mBg[i];
mBgOptions[i].inPreferredConfig = Bitmap.Config.RGB_565;
mBgOptions[i].inMutable = true;
mBgOptions[i].inSampleSize = 1;
The garbage collector no longer runs and the pauses have been removed.
As an f.y.i, inSampleSize has to be set or it won't work.
If you are targeting Android 3.0 then this answer may help:
How to load tiles from a large bitmap in Android?
Pre-Android 3.0 you could build an HTML page of your tiled images and let the built-in browser handle the load and unload of the images. My answer here has some more details:
How to tile and scroll a large image (10000x10000) in android
Anyone else have any alternative approaches?
Are you using android:largeHeap="true"? That might reduce the frequency of GCs. Also given you are targeting tablets, then you can safely assume that you are dealing with a concurrent garbage collector. So it will work best if it has more, smaller chunks of memory to collect, i.e. smaller tiles.
I used this easy to integerate source of WorldMap application:
https://github.com/johnnylambada/WorldMap
This uses a huge image of a world map, and uses cache to display a map.
To integerate, I just copied all the java files (5 i guess) and used the surfaceView in my layout file. Then I went through the small OnCreate() method of ImageViewerActivity.java and used the code in my activity (with sligh alteration, depending on my personal use).
Related
My app takes an image using the device camera, then opens it so the user can perform some processing. To do this I check the image orientation and rotate it if necessary, which I do like this:
Bitmap image = MediaStore.Images.Media.getBitmap(this.getContentResolver(), lastTaken);
int rotation = getCameraPhotoOrientation(filePath);
if (0 < rotation)
{
Matrix mx = new Matrix();
mx.setRotate((float) rotation);
image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), mx, true);
}
This code works on most devices, however on one of the older test devices I have been using, it causes an OutOfMemoryException when the image is rotated, presumably because it is trying to hold two copies of the image in memory.
How can I avoid this?
If there isn't an easy way to prevent the problem at this point in the process, is there a way that I can test how much memory I have available and limit the image resolution the camera uses to one that will leave enough room for this operation? I'm already specifying resolution, so it is really a question of how I can compare the image size and available memory to figure out what resolution would be safe. My one constraint is that the changes the user makes have to be on the original image, so editing a smaller copy of the image won't cut it, unless there is an easy way to reflect those changes on the stored version. Consequently downsizing the image to something that can be supported in memory wouldn't be a disaster by any means.
What is the size of your image? Did you check for possible memory leaks in the App?
Anyways, one way is, you can try to reuse the memory allocated to the first Bitmap after a new second Bitmap content is created.
As per Android docs here - https://developer.android.com/topic/performance/graphics/manage-memory ,
Android 3.0 (API level 11) introduces the
BitmapFactory.Options.inBitmap field. If this option is set, decode
methods that take the Options object will attempt to reuse an existing
bitmap when loading content. This means that the bitmap's memory is
reused, resulting in improved performance, and removing both memory
allocation and de-allocation.
You can set your Bitmap image to be recycled when a new Bitmap is created.
I couldn't find a way to figure out the amount of memory available and how much I would need, but after finding some other questions on this topic I found that the best solution for me was to enable the largeHeap element in the application manifest - that resolved the crash for me, although it didn't answer the question and after further reading I suspect there is no satisfactory way to do this as things stand.
I'm having serious issues with memory, and I'm looking forward to recycle bitmaps.
However, what my app is doing atm, is building a Gallery (yeah, the deprecated) with several bitmaps, and then, several Galleries.
So at the end of the day, the app looks like (LinearLayout style):
*Some webviews
*A Gallery with 7 images.
*Some webviews
*A Gallery with 10 images
and so on.
So what I'm thinking is... Once I've displayed those images, and them are on the screen, can those bitmaps be recycled?
Is there any way to recycle a whole Gallery component?
Thank you so much.
Edit:
I've tried soo many things. I'm still getting the error java.lang.IllegalArgumentException: bitmap size exceeds 32bits
This is my actual code:
BitmapFactory.Options bfOptions=new BitmapFactory.Options();
bfOptions.inDither=false; //Disable Dithering mode
bfOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
bfOptions.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
bfOptions.inTempStorage=new byte[32 * 1024];
bfOptions.inInputShareable=true;
bfOptions.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeFile(mUrls.get(position).getPath(), bfOptions);
inflatedImageView.setImageBitmap(Bitmap.createScaledBitmap(bitmap, 120, 120, false));
As you can see, I've set many options to the Bitmap, aswell as resizing it, and lowering it's quality. Still happenning the same issue.
First you should never manually call system.gc, and generally there is no need to manually recycle bitmaps either. Let the operating system do what its best at, deciding when to take the huge hits to run garbage collection.
One of the reasons that gallery was deprecated is I am fairly sure it just loads all items as soon as it renders. And does not load them and recycle them on demand like a list view. Which means it is a huge waste of RAM and no amount of recycling will help you. In addition Gallery is very glitchy on 4.0+ phones, which is the majority of the Android user base at this point. I would strongly recommend you move away from using gallery.
There are two alternatives.
1) View Pager which is now built into the Support V4 library
2) HorizontalListView --> https://github.com/MeetMe/Android-HorizontalListView
Also be absolutely sure you are downsampling all images prior to loading them in the image views. To learn how read http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Ok. I solved the issue I was having; I had a linearLayout with gallery components and webviews. If I didn't render the webviews the app didn't crash.
So after some tweaks now it's working correctly without crashes:
wv_detail.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
wv_detail.setLayerType(View.LAYER_TYPE_NONE, null);
I am looking for a library or some idea on how I can load an image by parts from the disk or a url straight to the disk then in parts again to the ram. So the two ways I see that this can be done is loading the whole image onto the disk by reading and writing it from the url directly using the ram only for the buffer then when the image is on the disk some how creating bitmaps of only parts of the image, that way I DO NOT load it all and putting those in a ListView.
The issue is that I am dealing with extremely long images (10K + pixels long w/ a width of 4-600) and they hog up lots of ram if loaded all in one bitmap. I can not just scale them down like the Google android tutorial does in the handling large bitmaps section as that results in a width too small to deal with. So if I can somehow generate small bitmap slivers on the disk I can use them by loading them in a ListView preventing loading the image as a whole into ram.
Right now I am breaking the long images into slivers from a bitmap and I realized that that isn't really accomplishing what I am trying to do as the whole image is loaded into a bitmap in memory and is then broken up, then GC (So I am using up the full abmount of ram anyways). I am testing on a new top of the line android phone and the app works fine, but the heap size reaches 80mb+ with the larger images temporarily in ram as it breaks down the bitmap and this will be an issue with devices that have lower heap limits
You can try using this class, support from 2.3 http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html
If you are using Java you can work with InputStreamReader and OutputStreamWriter. Method read() accepts the buffer as one of the parameters, its length can be anything suitable. Of course you can create a new file for each buffer being written.
Is it what you're looking for?
edit
well its not. have you seen this Strange out of memory issue while loading an image to a Bitmap object ?
If you have control over the server from where you are fetching data, throw in another field in your response, such that it returns a thumbnail/smaller image. The server can then generate the required thumbnails for you, without you bothering about it.
Decoding bitmaps on the fly might be expensive, most of the times. If you can't change anything on the server, download and save the images, and after saving, generate their corresponding thumbnails and save them as well. In your list, use the thumbnails. Also, save the information about which images have been cached, and whose thumbnails you already have. This might look like a lot of work, but depending on the use case, this can be a better approach dealing with large images.
Problem with downsizing?
Well, you can come up with some kind of logic as to generate thumbnails, based on the original size of the image. For longer(vertically long images), you could use BitmpapRegionDecoder (http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html) as #Binh Tran has suggested.
Try maybe encoding the image to make the size small.
My app is loading a large image (a house floorplan), then drawing touch-reactive objects (furniture, lamps etc.) on the image. I have a base image file included with my app but the objects come from coords in my database.
I've successfully deployed the app in multiple iterations, but now I need to use larger base images and BitmapFactory is causing an OutOfMemory exception on many devices (both old and new devices; anything with < 32MB heap seems to crash). I've read the 157 OOM questions on SO, but I'm afraid the link they all seem to point to won't help me since resolution / zooming is critical to the app's function.
I've tried to test the device's available memory before loading, but the results are spotty at best (some devices like the galaxy S3 report plenty of heap but still crash). I've also tried decreasing resolution but the image became unusable when reduced to a safe size based on the above test.
Is there another way to implement this design without ever using bitmaps?
I need to:
Load large base image
Create clickable shapes on top of the base image, maintaining their position / scale relative to the base image
BONUS: in the iOS version of my app, I can do SVG-style text scaling so a long label on a small object will stay inside the object
instead of running across the map(and will be invisible until the
image is zoomed). Replicating this in android would make me a happy
code monkey.
I can post code if needed, but you've all seen it before (almost all of it came from SO).
Thanks in advance for reading, and for any help you can give.
you have a few options:
break your large image into tiles, load these tiles into an array, and move a camera object around and only load tiles that need to be drawn, as the comments suggest.
make your image small and scale it up using 'android:scaletype`
Draw lines and curves on a Canvas object at runtime.
Use OpenGL
The appropriate solution really depends on how you want it to look. Tiling will take more dev effort but will look better, just be careful that you are properly cleaning up any tiles that aren't being drawn...
dynamically scaling will be easier, but you cannot guarantee the image won't be blurry.
Drawing on a Canvas object at runtime could work well-- just use Lines of different width and circles and Rects etc.
Using OpenGL will have the steepest learning curve, and might be overkill. This depends on your purpose.
You might like to look into using a "largeHeap"
http://developer.android.com/reference/android/R.styleable.html#AndroidManifestApplication_largeHeap
Here are some options:
1) Use tiles. Use tiles and dynamically load your data. Honestly, this is the best solution. With this solution you can load arbitrarily large images.
I've successfully used this approach for an endless paint canvas and it works quite well. You really only need to draw what is directly visible to the user. Tiles is a way to cast away pieces you don't need. A pyramid of tiles (where you pre-downsample your images and create more tiles), allows you to do this in a clean and fast way.
2) Use native code. The memory restrictions on native code are not the same as Java code. You can get away with allocating more memory.
3) Use OpenGL. Once again, the memory restriction for OpenGL are not the same as Java code.
4) Convert your original plan to an SVG and use an SVG library like this one.
5) Use "largeHeap". I strongly discourage this, as I think a largeHeap is rarely the solution, there are generally cleaner ways to approach the problem.
if the image is static , you might wish to use this nice library:
https://github.com/ManuelPeinado/ImageLayout
if the library doesn't support auto-downsampling of the image, you should do it by yourself, in order to use the best image for the current device (so that you won't get OOM).
for auto-sizing text , you might have some luch with the next post:
Auto-fit TextView for Android
I'm working on an image processing application for Android that recognizes music notation from pictures taken of music sheets.
I tried to load the entire image into a Bitmap using the BitmapFactory.decodeFile(imgPath) method, but because my phone doesn't have enough memory I get a "VM heap size" error. To work around this, I'd like to chop the full image into smaller pieces, but I'm not sure how to do that.
I also saw that it was possible to reduce the memory size of the Bitmap by using the inSampleSize property of the BitmapFactory.Option class, but if I do that I won't get the high resolution image I need for the music notation recognition process.
Is there anyway to handle this without going to NDK?
Android 2.3.3 has a new API called android.graphics.BitmapRegionDecoder that lets you do exactly what you want.
You would for instance do the following:
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(myStream, false);
Bitmap region = decoder.decodeRegion(new Rect(10, 10, 50, 50), null);
Easy :)
If it's from a camera the image will likely be jpeg format. You could use an external jpeg library - either in java or via the NDK, whatever you can find - to give you better control and load it a piece at a time. If you need it as an android.graphics.Bitmap then I suspect you will then need to re-encode the subimage as PNG or JPEG and pass it to BitmapFactory.decodeByteArray(). (If memory is a concern then do be sure to forget your references to the pieces of the bitmap promptly so that the garbage collector can run effectively.)
The same technique will also work if the input graphic is PNG format, or just about anything else provided you can find suitable decode code for it.
I think that by loading the image piecewise you are setting yourself an algorithmic challenge in deciding what parts of it you are really interested in the full detail of. I notice that BitmapFactory.Options includes the option to subsample, that might be useful if you want to analyse an overview of the image to decide what regions to load in full detail.
If you're dealing with JPEG images, see my answer to this question as well as this example.
I don't know how possible it is to get libijg on Android, but if it is, then it's worth a shot.