Android Bitmap too heavy error - android

I have the following problem that some of you must know on my android app :
3288-byte external allocation too large for this process.
Out of memory: Heap Size=5959KB, Allocated=3922KB, Bitmap Size=18614KB
VM won't let us allocate 3288 bytes
Facts :
I'm creating a bitmap of the screen (so quite huge) and I manipulate it (changing size etc ...) for doing a flipping page animation.
It crashes only on a desire HTC : on galaxy s2 and kindle fire, no problems.
I'm already desallocating the current Bitmap everytime I create a new one with the following code :
Bitmap old = this.bitmap;
this.bitmap = bitmap;
this.invalidate();
if(old != null)
old.recycle();
I also tryied to call this function :
public void recycle() {
if (this.bitmap!=null)
this.bitmap.recycle();
System.gc();
Runtime.getRuntime().gc();
}
Severals time in my code, and sometimes it gets slightly better (like it crashes a little later), but that's still not good.
I spent a lot of time on this problem, and I don't really get how to fix it. It's like on forum there is a lot of misinformation, so I'm kinda lost.
Thanks, ask for more precision.
Edit :
Here is a code called a lot :
//set the foreground image with the current day
Bitmap b = Bitmap.createBitmap(visibleLayout.getWidth(), visibleLayout.getHeight(),Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
visibleLayout.draw(c);
viewBitmapNext.setBitmap(b);
viewBitmapNext.setVisibility(View.VISIBLE);
Where viewBitmapNext is an overwritted element of the View class. The setBitmap function is described above.
About the resizement, I do this line of code :
viewBitmapPrevious.setLayoutParams(new RelativeLayout.LayoutParams((int) (iterator - ((totalWidth - iterator) - activity.getResources().getDimension(R.dimen.margin_right))/2), RelativeLayout.LayoutParams.WRAP_CONTENT));
Again, tell me if you you want to know more.

I found out what was the problem. It will not be interresting for anyone, because it's a dumb error closely related to my project, but I say it anyway.
I actually had 2 errors :
one loop creating elements infinitly.
Two big pictures I put as a background after a certain action performed on a cheap phone ( I'm still on it but it should be easy to solve). I'll edit this answer when it's done.
To everyone that helped me, you couldn't find out the problem's solution (wasn't related to the bitmap-screen I do), but still it was helpful on it's way.
Thanks.

Related

Erratic errors loading image with Texture2d.LoadImage(byte[]) on Android devices

I have an app which has to create Sprite-instances on the fly based on data contained in byte arrays (PNGs and JPGs). The following code is used to create the sprites:
Texture2D texture = new Texture2D(2, 2, TextureFormat.RGBA32,false,false);
texture.LoadImage(data);
Vector2 pivot = new Vector2(0.5f, 0.5f);
Rect tRect = new Rect(0, 0, texture.width, texture.height);
return Sprite.Create(texture, tRect, pivot);
This works fine, however, depending on the device and the size of the images, after a random number of images, the app freezes and then will be shut down by the OS. Its always another image, which fails. Also, the data source is irrelevant.
Looking into the logs of the app via adb shows nothing. If I write to the debug log, I can see, that the last statement which gets called is texture.LoadImage. However, there is no exception or another information about the error. Catching the exception does also not work.
The error does not occur in the editor. The error occurs on the android devices (2) in development build and in production build.
Searching the web, I found the below entry, which states the very same problem, but no solution has been posted (they circled around the www-part, however the problem is not with that):
https://forum.unity.com/threads/android-crash-when-using-multiple-www.483941/
UPDATE
One interesting finding is, that if I set the markNonReadable-Parameter of the texture.LoadImage() method to true, the error occurs less frequently, but still is there.
texture.LoadImage(data,true);
Textures are not garbage collected. So if you create a texture using new Texture then you need to destroy the texture with Destroy(texture) when you no longer need it. I believe Sprite object also needs to be destroyed.
In your case, textures that were loaded stayed in memory until Android OS closed your app because of memory pressure.
UnloadUnusedAssets() should also destroy all the textures and sprites that are no longer referenced, but it takes a lot of time (about 1 second), so it only makes sense to call that when changing scenes.

Out Of Memory Error in Android Studio

I'm trying to create my first android app, so I'm really new to android development. I'm trying to make a nice background image for my app. I made an image that is the exact dimensions of the device, but when I try to load it, i get the error "Failed to allocate a 218748 byte allocation with 217220 free bytes and 212KB until OOM" Here's my code:
#Override
protected void onDraw(Canvas canvas) {
drawBackground(canvas);
protected void drawBackground(Canvas canvas) {
Bitmap background = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.background2));
canvas.drawBitmap(background, 0, 0, null);
I've tried scaling it down, which works, but then it doesn't fill the entire screen. How do I resolve this issue?
You should recycle bitmap after canvas.drawBitmap(background, 0, 0, null);, or create bitmap once, in the constructor, and then use it, it is very costly to create bitmap each time in the onDraw method, it even costly to create simple objects in the onDraw, you shuold avoid from it, android:largeHeap="true" and System.gc() can't help in this case. Read this
Custom Drawing
Creating objects ahead of time is an important optimization. Views are redrawn very frequently, and many drawing objects require expensive initialization. Creating drawing objects within your onDraw() method significantly reduces performance and can make your UI appear sluggish.
There is blog that will help you to detect and prevent Out of memory issue. I wrote this based on a real problem I faced and here is the link
If you got stuck anywhere in the tutorial please let me know we can discuss and if you want to add something please suggest.

Android - Picture callback data returns an image that it black or jumbled

So I am using the Android camera to take pictures within an Android app. About 90% of my users have no issues, but the other 10% get a picture that returns pure black or a weird jumbling of pixels.
Has anyone else seen this behavior? or have any ideas why it happens?
Examples:
Black:
Jumbled:
I've had similar problems like this.
The problem in short is: Missing data.
It occurs to a Bitmap/Stream if the datastream was interrupted for too long or it is accidentally no more available.
Another example where it may occur: Downloading and uploading images.
If the user disables all of a sudden Wifi/mobile network no more data can be transmitted.
You end up in a splattered image.
The image will appear/view okay(where okay means black/splattered, it's still viewable!) but is invalid internally (missing or corrupted information).
If it's not too critical you can try to move all the data into a Bitmap object (BitmapFactory.decode*) and test if the returned Bitmap is null. If yes the data possibly is corrupted.
This is just solving the consequences of the problem, as you can guess.
The better way would be to take the problem on the foot:
Ensure a good connection to your data source (Large enough, stout buffer).
Try to avoid unneccesary casts (e.g. from char to int)
Use the correct type of buffers (Either Reader/Writer for character streams or InputStream/OutputStream for byte streams).
From android 4.0 they put hardwareAcceleration set to true as default in the manifest. Hardwareaccelerated canvas does not support Pictures and you will get a black screen...
Please also check that whether you use BitmapFactory.Options object for generating the bitmap or not. Because few methods of this object also makes the bitmap corrupted.

A riveting mystery: Android, Bitmaps (JPEGs), and the GridView of DEATH?

My app is exhibiting some very strange behavior. I have tried everything to resolve the issues I'm having, but, at this point, I'm at a total loss.
Some background information is in order (some details of the implementation that I'm providing might be overkill, but I want to make sure no stone is left unturned):
The app makes use of the device's camera, utilizing a SurfaceView with the necessary SurfaceHolder callbacks to open the device's Camera and take photos. That all works just fine.
The photos are then saved to the devices internal storage, using a FileOutPutStream. I then maintain a reference to the file path of each photo inside a plain old Java object, which is actually serialized and also written to the device's internal storage using an ObjectOutputStream.
The photos, which are in JPEG format, are later retrieved by grabbing the serialized ArrayList of my objects, grabbing the file path from those objects and passing it to my caching mechanism like so Bitmap bm = ImageCache.getSharedInstance().getBitmap(photoObject.filePath,getResources()); to return a Bitmap which I can use with an ImageView, whatever. I use this to for displaying the JPEGs in ListViews, GridViews, etc.
Here's the problem:
I have a View, which I've created programmatically, that has a sort of navigation bar at the top of it. All the navigation bar does is set the "main" part of my custom View class to display whatever other view I pass to it. So, my View class has a method like: setContent(View v) which simply removes all Views from the main part (or the area that I've carved out as the content area) and then adds the View you passed in to this content area.
So, if you click on nav button, it shows the photos along with some other data in a ListView, click another and it shows a GridView using the same photos. Right here is the problem. I like to call it THE GRIDVIEW OF DEATH. The GridView implementation is pretty straightforward and seems to work just fine when it's loaded.
mGalleryAdapter = new GalleryAdapter(MyActivity.this, list);
gridView.setAdapter(mGalleryAdapter);
Fine... but here's the issue. Once the GridView is loaded and displays the data from the adapter, if you try to start any other activity or navigate to any other are of the app, the activities in question seem to launch, but the Views inside of them never display. It is the strangest behavior. If you start with the ListView displaying and then navigate to some other tab (yea, I'm using tabs to sort of mimick iPhone style UITabs), everything is fine. The activity loads, the views display... everything is fine. But once you click to display the GridView, the app just seems to melt. Aaaaand, here's the catch. Whatever activity you've navigate to after displaying the GridView, will first display an empty, white activity (with no view). But if, for example, you long-press the home button and switch to some other running app and then come back to my app, the newly launched activities then display the views inside of them just fine and the app resumes normally. Strange indeed.
I must mention that I originally had the app crash several times because of "exceeding the VM budget." This was clearly related to issues with the JPEGs and their memory usage. From the stuff I've read online, the main fix to this is to crank up the BitmapFactory.Options sampleSize. Well, that's exactly what I've done. I've cranked it way up to reduce the image size:
public Bitmap getBitmap(String filepath, Resources resources){
SoftReference<Bitmap> reference;
if(resourcesLoaded.containsKey(filepath)){
reference = resourcesLoaded.get(filepath);
if(reference.get() == null){
int width = resources.getDisplayMetrics().widthPixels;
int height = (int) (42*((float)(((float)width)/320.0)));
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 28;
opts.outHeight = height;
opts.inTempStorage = new byte[16*1024];
opts.outWidth = width;
opts.inPurgeable = true;
Bitmap bit = BitmapFactory.decodeFile(filepath ,opts);
reference = new SoftReference<Bitmap>(bit);
resourcesLoaded.put(filepath, reference);
}
}else{
int width = resources.getDisplayMetrics().widthPixels;
int height = (int) (42*((float)(((float)width)/320.0)));
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.outHeight = height;
opts.outWidth = width;
opts.inTempStorage = new byte[16*1024];
opts.inSampleSize = 28;
opts.inPurgeable = true;
Bitmap bit = BitmapFactory.decodeFile(filepath ,opts);
reference = new SoftReference<Bitmap>(bit);
resourcesLoaded.put(filepath, reference);
}
return reference.get();
}
And, that's the weird thing, the app doesn't even crash now. Using caching and reducing the image size seems to have eliminated that crash (at least on some of the devices I've tested on), but this strange behavior, WHICH ONLY OCCURS ONCE LOADING THE GRIDVIEW OF DEATH and then navigating away from it, persists.
On a related note, I have used some third party app on my phone to see the current memory usage of my app and, at times, it creeps up to 100MB! Every time I take a photo with the app and save it to the internal storage, the memory usage seems to jump considerably (generally starts around 30-40MB and climbs from there). So clearly, there are memory issues going on. But again, the app won't crash with a "exceeded VM budget" error. On the other hand, it's hard to ignore the obvious memory issues with the app.
I fully realize that some of my implementation is not perfect, not super memory efficient, and that using singletons to hold the ArrayList of my objects, etc might not be the greatest way to go. But even still, this behavior seems inexplicable.
To summarize: the app will load completely blank activities ONCE AND ONLY ONCE you've clicked to display the images (that are stored as JPEGs on the device) in the GridView. If you never click to use the GridView, everything is fine.
What's with the Gridview of death?
(I'm happy to provide the code for any portions you think are relevant.)
UPDATE:
Thanks #CommonsWare for all your patience and advice. Really appreciate it and it's always nice to have the unofficial Android developer advocate on the case. Turns out that using Fragments did, in fact, solve all of my problems. For a moment, I thought I had fully implemented the Fragment solution, but quickly realized that I was still "setting the content" of some base View that was loaded in my Fragment to my GridView. By creating a new framgent containing the Grid View and loading it, all problems seem to be solved and the app is now stable!
UPDATE: Well, I spoke too soon :( Apparently this GridView is still causing problems. Now when I switch to a new tab, I can see that the button I'm using for the tab was clicked, but when it calsl setCurrentTab() nothing happens. App remains frozen in the GridView screen.

Preventing "flickering" when calling Drawable.draw()

I have a little experimentation app (essentially a very cut-down version of the LunarLander demo in the Android SDK), with a single SurfaceView. I have a Drawable "sprite" which I periodically draw into the SurfaceView's Canvas object in different locations, without attempting to erase the previous image. Thus:
private class MyThread extends Thread {
SurfaceHolder holder; // Initialised in ctor (acquired via getHolder())
Drawable sprite; // Initialised in ctor
Rect bounds; // Initialised in ctor
...
#Override
public void run() {
while (true) {
Canvas c = holder.lockCanvas();
synchronized (bounds) {
sprite.setBounds(bounds);
}
sprite.draw(c);
holder.unlockCanvasAndPost(c);
}
}
/**
* Periodically called from activity thread
*/
public void updatePos(int dx, int dy) {
synchronized (bounds) {
bounds.offset(dx, dy);
}
}
}
Running in the emulator, what I'm seeing is that after a few updates have occurred, several old "copies" of the image begin to flicker, i.e. appearing and disappearing. I initially assumed that perhaps I was misunderstanding the semantics of a Canvas, and that it somehow maintains "layers", and that I was thrashing it to death. However, I then discovered that I only get this effect if I try to update faster than roughly every 200 ms. So my next best theory is that this is perhaps an artifact of the emulator not being able to keep up, and tearing the display. (I don't have a physical device to test on, yet.)
Is either of these theories correct?
Note: I don't actually want to do this in practice (i.e. draw hundreds of overlaid copies of the same thing). However, I would like to understand why this is happening.
Environment:
Eclipse 3.6.1 (Helios) on Windows 7
JDK 6
Android SDK Tools r9
App is targetting Android 2.3.1
Tangential question:
My run() method is essentially a stripped-down version of how the LunarLander example works (with all the excess logic removed). I don't quite understand why this isn't going to saturate the CPU, as there seems to be nothing to prevent it running at full pelt. Can anyone clarify this?
Ok, I've butchered Lunar Lander in a similar way to you, and having seen the flickering I can tell you that what you are seeing is a simple artefact of the double-buffering mechanism that every Surface has.
When you draw anything on a Canvas attached to a Surface, you are drawing to the 'back' buffer (the invisible one). And when you unlockCanvasAndPost() you are swapping the buffers over... what you drew suddenly becomes visible as the "back" buffer becomes the "front", and vice versa. And so your next frame of drawing is done to the old "front" buffer...
The point is that you always draw to seperate buffers on alternate frames. I guess there's an implicit assumption in graphics architecture that you're always going to be writing every pixel.
Having understood this, I think the real question is why doesn't it flicker on hardware? Having worked on graphics drivers in years gone by, I can guess at the reasons but hesitate to speculate too far. Hopefully the above will be sufficient to satisfy your curiousity about this rendering artefact. :-)
You need to clear the previous position of the sprite, as well as the new position. This is what the View system does automatically. However, if you use a Surface directly and do not redraw every pixel (either with an opaque color or using a SRC blending mode) you must clear the content of the buffer yourself. Note that you can pass a dirty rectangle to lockCanvas() and it will do the union for you of the previous dirty rectangle and the one you are passing (this is the mechanism used by the UI toolkit.) It will also set the clip rect of the Canvas to be the union of these two rectangles.
As for your second question, unlockAndPost() will do a vsync, so you will never draw at more than ~60fps (most devices that I've seen have a display refresh rate set around 55Hz.)

Categories

Resources