I have a AnimationDrawable that i initialise before starting the animation and recycle it right as it finishes.. the problem is that when i want to start the animation again, i reinitialise everything but still gives an exception
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap#2bbad018
here's my code
public ImageView radarImageView;
public AnimationDrawable animationDrawable;
public void animationStart() {
// animationStop();
radarImageView = (ImageView) findViewById(R.id.radarIV);
radarImageView.setBackgroundResource(R.drawable.sensor_animation);
animationDrawable = (AnimationDrawable) radarImageView.getBackground();
animationDrawable.start();
}
public void animationStop() {
animationDrawable.stop();
for (int i = 0; i < animationDrawable.getNumberOfFrames(); ++i){
Drawable frame = animationDrawable.getFrame(i);
if (frame instanceof BitmapDrawable) {
((BitmapDrawable)frame).getBitmap().recycle();
}
frame.setCallback(null);
}
animationDrawable.setCallback(null);
// animationDrawable = null;
// radarImageView.setBackgroundResource(0);
}
Why does it not reinitialise the whole thing again?
The problem is because when you Bitmap.recycle() is called on the bitmap you cannot reuse it.
Also as you are calling setBackgroundResources(R.drawable.sensor_animation) you are referring to the same object which its bitmaps were recycled previously.
As a solution, you either have to create a new drawable every time or do not recycle that instance.
If you are worries about the memory usage try to use smaller bitmaps. Also using the correct sizes for different screen densities would help.
Related
It appears that, when I call setImageDrawable(null) for an ImageView, the bitmap is not being released. What can I do to force it to free the bitmap memory?
I have an activity that needs to display a large number of drawables, but not at the same time. I have extended ImageView as shown here. The XML layout file declares instances of this, with the "src" attribute set to "#null". In replacement, I have a custom attribute to hold the drawable resource id.
public class ImageViewHolder extends ImageView
{
int srcId = 0;
//-----------------------------------------------------------------------------
public ImageViewHolder (Context context, AttributeSet attrs)
{
super (context, attrs);
TypedArray a = context.obtainStyledAttributes (attrs, R.styleable.ImageViewHolder, 0, 0);
srcId = a.getResourceId (R.styleable.ImageViewHolder_src, 0);
a.recycle();
}
//-----------------------------------------------------------------------------
public void showDrawable (boolean makeVisible)
{
if (makeVisible)
{
// Drawable d = getResources().getDrawable (srcId);
// setImageDrawable (d);
setImageResource (srcId);
}
else // hide & free memory
{
Bitmap bitmap = ((BitmapDrawable)getDrawable()).getBitmap();
setImageResource(0);
bitmap.recycle();
// setImageDrawable (null);
}
}
}
I've tried using SetImagerResource() instead but get the same results. I also tried calling System.gc() after clearing the drawable and that only deferred the point where my device ran out of memory.
Any ideas?
Instead of using
setImageResource (srcId);
use
Resources r = getResources();
Bitmap b = BitmapFactory.decodeResource (r, srcId);
setImageBitmap (b);
This bypasses the Resources() caching and just recreates the bitmap each time you need it. After testing this code, I'm still eventually getting an out-of-memory error but it's taking much longer to get there. I'm going to assume it's being caused by something else. For now, that's enough time spent on this problem :)
I have got a Image (Bitmap) on a ImageView, without flickering. When I change something with setPixel(x, y, COLOR_VALUE), so some Pixels are changed on the ImageView, it begins to flicker, where I changed the Pixels.
public class Drawer extends ImageView {
private Bitmap someBitmap;
public void doSomeDrawing() {
for (int i = 0; i < 100; i = i + 2) {
someBitmap.setPixel(x, y, COLOR_VALUE);
}
setOnDraw();
}
public void setOnDraw() {
this.setImageBitmap(someBitmap);
}
Try getting a copy of your bitmap and draw on it. Then recycle your old bitmap.
The problem here might also be that setting the pixel takes time and if you do this on the UI Thread, it will slow down your app and may cause flickering too. How much time does doSomething take ?
I have some CustomButton, and at some point, a background is drawn from a base64String into a BitmapDrawable, which is applied to the CustomButton. Within the total lifespan of the application, this can happen multiple times. This caused a OutOfMemoryError: bitmap size exceeds VM budget, and after some digging on StackOverflow, this happened because the Bitmaps were not recycled. So I tried to handle this with some code that is executed each time another background was applied (this code comes from the CustomButton class):
public void recycleBackground() {
BitmapDrawable bd = null;
Drawable bg = getBackground();
if (bg != null) {
bg.setCallback(null);
if (bg instanceof BitmapDrawable) {
bd = (BitmapDrawable) bg;
if (!bd.getBitmap().isRecycled()) bd.getBitmap().recycle();
}
bd = null; //just precautionous
bg = null; //also just a precaution
}
Yet, the memory is slowly (because background images are not all that large) but surely flooded. What am I missing or doing wrong? I combined the above code from some different questions/answers from SO. I can find a lot on recycling Bitmaps, not so much on the recycling of Drawables. Maybe that's what I'm doing wrong?
my live wallpaper doesn't slide smoothly most of the time on home screen change.
there is not a complicate coding for the position of background. it just use the xpixles that located on the onoffsetschanged method to adjust the x position of the wallpaper.
sometime it slide well same as in the sun rise live wallpaper app. but most of the other time it slide.. dunno how to describe it but its like a static sharp move. is there any idea about that guys.
best regards.
private void drawS() {
final SurfaceHolder holder = getSurfaceHolder();
canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas != null) {
background = BitmapFactory.decodeResource(getResources(),
R.drawable.background);
canvas.drawBitmap(background, mXPixels, mYPixels, null);
}
}
finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
handler.removeCallbacks(runnableS);
if (visible) {
handler.postDelayed(runnableS, 25);
}
}
try to avoid resource creation in draw routine; remove this from drawS() function:
background = BitmapFactory.decodeResource(getResources(),
R.drawable.background);
and put it wherever you initialize your stuff and just use reference to background Bitmap later on.
If you take a look at logcat you'll see that there's a lot or resource shuffling and memory management going on (allocating memory for your resource, decoding bitmap image in it etc) every time you call your drawS(); ie every time you draw your background...
Cheers,
Ivica
I encountered the same problem and solved by following Ivica suggestion.
I moved the bitmap creation line inside the surface creation sub:
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
mybackground = BitmapFactory.decodeResource(getResources(), R.drawable.mybg);
}
In this way the sliding is very fluent
I'm working on a game that requires drawing to SurfaceView's
Canvas. I'm using SurfaceHolder to obtain Canvas to draw to, and
perform drawing in a separate thread. Everything I draw displays
correctly except for this one animation.
Here is the animation definition (it is in res/drawable/
my_animation.xml:
and here is how I draw it to the Canvas:
AnimationDrawable ad = (AnimationDrawable)getResources().getDrawable(R.drawable.my_animation);
ad.draw(canvas);
ad.start();
but only the first frame ever appears on the screen.
What am I missing?
Thank you
If I get the reference right, you have to assign your AnimationDrawable to an ImageView as the example from http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html demonstrates:
// Load the ImageView that will host the animation and
// set its background to our AnimationDrawable XML resource.
ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image);
img.setBackgroundResource(R.drawable.spin_animation);
// Get the background, which has been compiled to an AnimationDrawable object.
AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();
// Start the animation (looped playback by default).
frameAnimation.start()
In your case you did not assign your AnimationDrawable to any view so I suppose that's why it does not get updated. So you have to call AnimationDrawable.getFrame(int) manually.
If you are already using a separate thread why don't you involve also the code for your animation? This would render your code more consistent in my opinion.
Although this is an old question, i run into this problem too and figure out a walkaround, so i just post it here in case anyone searching about.
```java
final AnimationDrawable animationDrawable = ...;
final View v = new View(this) {
#Override
protected void onDraw(Canvas canvas) {
animDrawable.draw(canvas);
}
};
animationDrawable.setCallback(new Callback() {
#Override
public void unscheduleDrawable(Drawable who, Runnable what) {
v.removeCallbacks(what);
}
#Overridew
public void scheduleDrawable(Drawable who, Runnable what, long when) {
v.postDelayed(what, when - SystemClock.uptimeMillis());
}
#Override
public void invalidateDrawable(Drawable who) {
v.postInvalidate();
}
});
```
Animated drawables of all kinds (Transition, etc.) need a Callback to schedule redrawals. As zhang demonstrates, it is a simple interface for posting Runnables.
To support animated drawables in a custom view, you need two things:
pass your View to your animated Drawable's setCallback. Even base View implements Drawable.Callback, so there's no need to write your own wrapper.
override verifyDrawable to also return true for your animated Drawable.
As to why anyone would need that - I have just implemented animated drawable support in RecyclerView.ItemDecorations, how cool is that :-)
You are working with Canvas means, you are coding in low level. In low level u have to do everything. working with View (ImageView) means, it is high level.In ImageView already defined in low level on canvas how to deal with AnimationDrawable.
Lars Blumberg's answer is correct but the documentation is incorrect; you also need to take into account this: animation start issue