I want to do Frame Animation with 30 frames.
My code for the same is:
animation = new AnimationDrawable();
for (int i= 1; i<= 30 ; i++){
frame= "xyz"+ i;
resource = this.getResources().getIdentifier(frame, "drawable", "com.example.frameanimation");
Log.e("123", "resource value======"+resource+"============"+frame);
animation.addFrame(getResources().getDrawable(resource), 50);
System.gc();
}
animation.setOneShot(false);
In this code I have included System.gc();. Will this help me in optimizing in memory related issues. Or it can still give memory related issues. And does this mean that 30 images which are present in memory while frame animation will be destroyed.?
Is there any other way to optimize this?
Suggestions please.
Thanks in Advance.
System.gc() is a hints to garbage collector. It means garbage collector may not collect garbage after executing this line of code.
Good way to optimize this is to optimize the drawable you are loading. You can load based on screen DPI. It is not useful to load a large image inside a small screen. Read this post about image loading link.
You can also try, use the main image as background and only animated part inside animation.
Alternatively, you can try using SoftReference. If you run into heap memory issue this will at least solve this problem.
Related
I have an activity that show some full screen images in crossfade. There are a total of 6 images. To do this I used 2 ImageViews and 2 animation playing at the same time, one that fades out the first image and one that fades in the second. I used this video as a reference https://www.youtube.com/watch?v=9XbKMUtVnJA
Because I need this to run continuously, I used a timer to schedule the animation every 4 seconds. Everything works, but it uses a huge quantity of memory. To load the images I tried:
the basic not recommended way used in the video, i.e loading all images in a drawable array
setting the images using imageview.setImageResource
loading the images from assets using Picasso
All this method are memory intensive a cause an out of memory exception on older devices (like a Galaxy S2). The Picasso approach isn't working properly.
I'm sure there's a better way of doing this, but I don't know it, any suggestions?
Here's the relevant code:
private void animateImageview(){
prevImageView.animate().alpha(0);
nextImageView.animate().alpha(1).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mCurrentDrawable =
(mCurrentDrawable + 1) % imagesToShow.length;
int nextDrawableIndex =
(mCurrentDrawable + 1) % imagesToShow.length;
prevImageView.setImageResource(imagesToShow[mCurrentDrawable]);
nextImageView.setImageResource(imagesToShow[nextDrawableIndex]);
nextImageView.setAlpha(0f);
prevImageView.setAlpha(1f);
}
});
protected void onCreate(Bundle savedInstanceState) {
...
prevImageView.setImageResource(imagesToShow[0]);
nextImageView.setImageResource(imagesToShow[1]);
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
animateImageview();
}
}, 5000, 4000);
}
Edit:
the largeHeap option in the manifest seems to solve the problem, but I'm not convinced that I'm doing this thing right anyway.
Edit2:
A gist with the solution I used https://gist.github.com/alexmazza/003e3449c02fe58848a9
Please remove the largeHeap option from the manifest. It's not fixing the problem, it's sweeping the problem under the rug.
This is the problem
The images are 1536x2048 loaded in an imageview
You shouldn't just try to load the image at maximum resolution, because, as you said, in a S2 for instance, which has a display of 800 x 480, what's the point?
Generally speaking, what you should do instead is load a bitmap into memory as big as you need it to be (in your case it should be as big as the ImageView). You can follow a very good tutorial for this in the official docs.
To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it, unless you absolutely trust the source to provide you with predictably sized image data that comfortably fits within the available memory.
[....]
For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView.
Hope this helps.
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.
I get this error all the time. And as I can see, there are a lot of questions already on stackoverflow.com, but sadly, I don't find any answers which will suit me.
I have 60 PNG images (2,5MB all together) which I would like to put it in animation.
I tried with three different ways.
1
mAnimation = new AnimationDrawable();
mAnimation.addFrame((BitmapDrawable)getResources().getDrawable(R.drawable.yawning_00001), FPS_12);
...
mAnimation.addFrame((BitmapDrawable)getResources().getDrawable(R.drawable.yawning_00063), FPS_12);
mAnimation.start();
2
XML
<animation-list android:oneshot="true">
<item android:drawable="#drawable/yawning_00001" android:duration="83" />
...
<item android:drawable="#drawable/yawning_00063" android:duration="83" />
</animation-list>
Java
ImageView img = (ImageView)findViewById(R.id.animation);
img.setBackgroundResource(R.drawable.yawning);
AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();
frameAnimation.start();
3
With class extending ImageView (I will just show important stuff here)
public void loadAnimation(String prefix, int nframes) {
mBitmapList.clear();
for (int x = 0; x < nframes; x++) {
String zeros = "000";
if (x < 10) {
zeros += "0";
}
String name = prefix + "_" + zeros + x;
Log.d(TAG, "loading animation frame: " + name);
int res_id = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
d = (BitmapDrawable) mContext.getResources().getDrawable(res_id);
mBitmapList.add(d.getBitmap());
}
}
In all cases I get the same error... All some around after 15 picture loads.
E/AndroidRuntime(1591): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
I am begging to wonder if this is frame animations are even possible in Android.
Does anybody maybe have a alternative to frame by frame animation? If yes, please link to any showcase.
You will need to recycle your images in some way because you won't have enough memory ever for 60 images.
You think your images are 2,5 meg all together but this the compressed png version of your files.
If you want to know how much memory you are using with your files when uncompressed in bitmap format in memory just do : width*height*number of images*bytes per pixel....Then you'll know why you crash :D
AnimationDrawable are not meant for that kind of heavy usage. You should start looking at SurfaceViews and then you'll be free to implement whatever memory management method you want to use to display your animation.
http://developer.android.com/reference/android/view/SurfaceView.html
Good luck.
Sometimes, The memory leak come from some where which isn't the line of code show in stack trace. I think you should read below article carefully then check your own code to omit some special issue, such as:
Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
Try using the context-application instead of a context-activity
Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance**
Article
EDIT:
You also should check these:
Call recycle() to remove unused bitmap
use sampleSize > 1 to reduce bitmap size.
Bitmap.createBitmap(width,
height, new BitmapFactory.Options().inSampleSize=4)
My game uses surfaceview(I know I should use GL).
I draw alot of bitmaps to my game character world controll so on. And i run into this when I opened my LogDog:
08-05 10:17:29.151: ERROR/dalvikvm(24048): Out of memory: Heap Size=5379KB, Allocated=2735KB, Bitmap Size=20576KB, Limit=32768KB
I dont know if it is leak or what.
My Allocation Tracker shows:
like 30:
138 96 char[] 9 android.content.res.AssetManager getCookieName
Then
Tons of:
32 80 android.graphics.BitmapFactory$Options 9 android.graphics.BitmapFactory decodeResource
And last:
Like 30:
141 56 android.graphics.Bitmap 9 android.graphics.BitmapFactory nativeDecodeAsset
And also some more of simular ones.
Here is some code that I think drains my memory:
player = BitmapFactory.decodeResource(getResources(), R.raw.ghostright);
world = BitmapFactory.decodeResource(getResources(), R.raw.lvl2);
thumb = BitmapFactory.decodeResource(getResources(), R.raw.thumb);
resized = Bitmap.createScaledBitmap(player, width/10, width/6, false);
player = resized;
resized = Bitmap.createScaledBitmap(world, height*10, height, false);
world = resized;
resized = Bitmap.createScaledBitmap(thumb, height/6, height/6, false);
thumb = resized;
I heard that I should use resycle but I dont know where because I always use the bitmaps
//Simon
PS: I really need help -.-
I use a lot of bitmaps on SurfaceView too and don't have this problem.
When it comes to animated sprites, you can use a sprite sheet rather than load them individually frame by frame.
You don't need to use the reference "resized" you can just say:
player = Bitmap.createScaledBitmap(player, width/10, width/6, true);
the old bitmap will lose its reference and be collected by GC. Note that I placed TRUE for bitmap filtering when rescaling to make a better quality.
On some devices onSizeChanged can happen two times which may resize bitmaps twice, if that is where you are doing your scaling.
The format of loaded bitmaps does metter whether it is ARGB_4444 or ARGB_8888 etc So you may need to explore this option and if you can use a format which requires less memory yet it has a good enough quality for your game. Of course the rule is not to load images into memory bigger than they are needed and when they are needed.
It dosen't have to be a memory leak, it could just be that you have so large bitmaps that they want to allocate to much memory. Here's a good method to determine how much memory a bitmap is going to take: W*H*8. So if you have a bitmap that's 300*300 px it's 300*300*8 = 720 kb.
Figure out how much allocated heap you have at any given time and see if it increases with time even though you know that you're not allocating new bitmaps. If so, then yes, you have a memory leak. If, however, your app crashes right on startup, then you are probably just exceeding heap limit.
I am creating a viewer application that calls BitmapRegionDecoder.decodeRegion(Rect, BitmapFactory.Options). I am disposing of the bitmap previously got from decodeRegion before each call:
//this is a function I wrote that gets the rectangle I need
//from the zoom/pan state of a lower-resolution ImageView (page).
//it is bragable.
Rect areaRect = page.getBitmapRegionDecoderRect(areaBitmapRect);
BitmapFactory.Options options = new BitmapFactory.Options();
//area is the ImageView which will get the hi-res bitmap in it.
if(area.getHeight()!=0)
{
//I used Math.round because we are so desperate to save memory!
//The consequent blurring is actually not too bad.
options.inSampleSize = Math.round( ((float) areaRect.height()) / ((float) area.getHeight()) );
}
else
{
options.inSampleSize = Math.round( ((float) areaRect.height()) / ((float) page.getHeight()) );
}
if(options.inSampleSize==0) options.inSampleSize=1;
if(options.inSampleSize>16) options.inSampleSize=16;
options.inPreferredConfig = Bitmap.Config.RGB_565;
if(areaRect.left<0) areaRect.left = 0;
if(areaRect.right>areaBitmapRect.right) areaRect.right = areaBitmapRect.right;
if(areaRect.top<0) areaRect.top = 0;
if(areaRect.bottom>areaBitmapRect.bottom) areaRect.bottom = areaBitmapRect.bottom;
if(areaBitmap!=null)
{
//recycling our garbage ... or are we?
areaBitmap.recycle();
areaBitmap = null;
try
{
//dirty hack
wait(200);
}
catch(Exception x)
{
//something happened.
}
}
//the all-important call
areaBitmap = areaDecoder.decodeRegion(areaRect, options);
area.setImageBitmap(areaBitmap);
I was having problems with the fact that on very quick successive UI events, we were running out of memory. As you can see I have "solved" this with the dirty hack (the thread waits 200ms and gives Android a bit of time to catch up).
I'm not very happy with this, for obvious reasons. Firstly, is my diagnosis (that garbage collection is not finishing before we allocate new memory) correct? Secondly, I tried putting
while(!areaBitmap.isRecycled())
around a counter increment after the recycle() call and the counter stayed at zero. I see the sense in having isRecycled() do this but I need something like an isCompletelyRecycled() method instead. Does Android have anything like this? Thirdly, if I can't get anywhere with this, is there an "available memory" method I can use to tell if my call is going to push us over? I can't find one. It would be nice if Android would say MORE CORE AVAILABLE BUT NONE FOR YOU so I could maybe call my wait loop as a plan B, or eventually try something less intensive instead.
Bitmap memory is allocated in the native heap, so System.gc() will not help. The native heap has its own GC, but clearly it is is not kicking in quickly enough for you - and I am not aware of any way to force it (ie there is not a native analogue of System.gc()).
You could change the structure of your application as Nicklas A suggests to re-use your bitmap.
Or you could use the native heap monitoring mechanisms detailed in BitmapFactory OOM driving me nuts to determine when it is safe to allocate a new bitmap.
Try running System.gc() instead of the sleep.
Actually I'm unsure if this will help as bitmap seems to be mostly native code meaning that it should be released as soon as recycle is called.
To me it seems like creating a new bitmap on every UI event seems like a bad idea, couldn't you just create one bitmap and edit that one?