I'm using animationdrawables in many activities of my app. This is the code to inicializate them:
public void prepareVideo (int resourceBall, String animation, int width, int height ){
imgBall = (ImageView)findViewById(resourceBall);
imgBall.setVisibility(ImageView.VISIBLE);
String resourceNameAnimation = animation;
int id_res = getResources().getIdentifier(resourceNameAnimation, "drawable", getPackageName());
imgBall.setBackgroundResource(id_res);
LayoutParams latoutFrame = imgBall.getLayoutParams();
latoutFrame.width = width;
latoutFrame.height = height;
imgBall.setLayoutParams(latoutFrame);
frame = (AnimationDrawable) imgBall.getBackground();
}
Then i call:
imgBall.post(new StartAnimation());
that call a runnable:
class StartAnimation implements Runnable {
public void run() {
frame.start();
}
}
The problem is i use many animations that are the uses the same xml (frame by frame). I have to call this method in many activities with the same animation. Finally i get a outofmemoryerror. I'm trying to free memory between screens:
for (int i = 0; i < frame.getNumberOfFrames(); ++i){
Drawable frame_rescue = frame.getFrame(i);
if (frame_rescue instanceof BitmapDrawable) {
((BitmapDrawable)frame_rescue).getBitmap().recycle();
}
frame_rescue.setCallback(null);
The problem is that when i try to use a resourde again y get other error:
"Trying to use recycled bitmap"
Anyone know other way of free memory?
I had the same issue, the problem was the runnable.
Making an independent thread to each animation made animationDrawables persist after destroying the activity and clearing views and callbacks.
So starting animations without independent threads lets GC collect unused resources.
Related
I am trying to introduce a delay between image change in a for loop. I tried Thread.sleep(1000), but the delay is introduced before the image change and after the total sleep period is over, the ImageView dispalys the final image. I tried a lot of solutions available on the internet but nothing works.
public void start(View view){
ImageView display = (ImageView)findViewById(R.id.imageView);
for (int i=0;i<6;i++){
display.setImageResource(R.drawable.dice1+i);
Thread.sleep(2000);
}
}
I need the iteration to wait for 2 seconds so that the image can be viewed.
Two problems.
1)Don't sleep on the main thread. Ever. Instead, create a Handler and use postDelayed with a Runnable that changes the image.
2)You can't do math on resource ids like that. You can't assume they'll be consecutive just because they're next to each other in the file. Create an array with the ids and use the image at index i.
You can get the sequence of image by the image name as you asked
private int image = 0;
private static final int MAX_IMAGES = 10;
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 55) {
image++;
if (image > MAX_IMAGES) {
image = 1;
}
imageView.setImageDrawable(getImageByName("dice" + image, TestActivity.this));
mHandler.sendEmptyMessageDelayed(55, 2000);
}
}
};
public static Drawable getImageByName(String name, Context context) {
Resources resources = context.getResources();
final int resourceId = resources.getIdentifier(name, "drawable",
context.getPackageName());
return ContextCompat.getDrawable(context, resourceId);
}
Now start the image sequence by calling
mHandler.sendEmptyMessageDelayed(55, 2000);
Your image will be changed per 2 secs
I think a CountDown timer can help in this situation. A few days ago I asked a similar question at stackoverflow, and I got this answer. I think it may be a good reference for you.
I'm trying to pinpoint this memory leak.
I have two SurfaceViews, A and B. I start A, then navigate to B, then press the back button to go back to A, and then I navigate to B again.
I can see my allocated memory rise each time I do this, and eventually I'll get an out of memory error.
Here is how I navigate to B, from inside the SurfaceView connected to A
Context context = this.getContext();
Intent i =new Intent(context, StartCareer.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);
In both views, I have a lot of Bitmaps drawing. In B, I can't find any references to A, and the only reference outside the context that I can think of is a reference to a Global class that I have. I also have some analytics stuff going on in the background. It could be a million different things, I'd imagine
I have the DDMS view on Eclipse up, but I'm not sure what I'm looking at, or how to find the exact object that keeps getting repeated.
I'd accept either a crash-course/tutorial on the DDMS Allocation Tracker, or someone to point out what I'm doing wrong.
Additional information:
I have some bitmaps being drawn on a SurfaceView. Examples of such from B are:
////At class level
Bitmap rightB,leftB;
////In the constructor
rightB = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.right), 100,75, true);
////In doDraw
canvas.drawBitmap(rightB, rbX, rbY, null);
And my onDestroys
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread.isAlive()){
mThread.setMenuRunning(false);
}
}
So I've run MAT and found one leak, at least. My Thread Keeps getting recreated.
Here's what's doing it.
#Override
public void surfaceCreated(SurfaceHolder holder) {
loading=false;
if (!mThread.isAlive()){
mThread = new ViewThread(this);
mThread.setMenuRunning(true);
mThread.start();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread.isAlive()){
mThread.setMenuRunning(false);
}
}
Assuming that these methods get called every time the view loses or gains focus, this seems obviously wrong. How can I re-organize this so that it's not?
Call this method in onDestroy() and onstop() of your app.
private void unbindDrawables(View view) {
Log.d(TAG,"in unbindDrawables");
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
view.setBackgroundResource(0);
Log.d(TAG,"removed views");
//finish();
}
}
A few hints:
Recycle bitmaps when done with an activity (onDestroy for example)
Use the application context rather than the activity itself as context whenever possible
Try to recycle() your bitmaps in onDestroy() of your activities.
What you need to do is explained in detail here. For your specific issue, you need to do this
// resize to desired dimensions
int height = b.getHeight();
int width = b.getWidth();
Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
height: " + height);
double y = Math.sqrt(IMAGE_MAX_SIZE
/ (((double) width) / height));
double x = (y / height) * width;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(b, (int) x,
(int) y, true);
b.recycle();
I have prepared no.of drawable animations.when application launch first animation will be start.i have two buttons(next and previous) with same activity.when i click on next button i got exception like,
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
My code is,
For getting drawable animations from drawable,
private void getAnimationForLetters(int mAnim) {
// TODO Auto-generated method stub
String anim = "capital_anim_" + new Integer(mAnim).toString();
final int resID = getApplicationContext().getResources().getIdentifier(
anim, "drawable", "my-package-name");
mImgLetter.clearAnimation();
mImgLetter.setBackgroundResource(resID);
mImgLetter.post(new Runnable() {
public void run() {
loadingAnimation = (AnimationDrawable) mImgLetter
.getBackground();
loadingAnimation.start();
}
});
}
my next button code is,
case R.id.btnNext_:
unbindDrawables(findViewById(R.id.imgLetter));
mLetterNum=mLetterNum+1;
getAnimationForLetters(mLetterNum);
my undind drawable method code is,
private void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
finally i got exception java.lang.OutOfMemoryError: bitmap size exceeds VM budget
It is showing exception here mImgLetter.setBackgroundResource(resID);
Please help me.
I have added following code to clear,
loadingAnimation.stop();
for (int i = 0; i < loadingAnimation.getNumberOfFrames(); ++i){
Drawable frame = loadingAnimation.getFrame(i);
if (frame instanceof BitmapDrawable) {
((BitmapDrawable)frame).getBitmap().recycle();
}
frame.setCallback(null);
}
loadingAnimation.setCallback(null);
It is working fine for only next or previous.First time click on next animation move to second animation,second time if i click on previous button i got exception like,
Canvas: trying to use a recycled bitmap android.graphics.Bitmap
please help me.
exactly - the system knows when to collect garbage, so calling that doesn't help in the least.
You have to really really manage your apps memory. 290x330 may not seem like much, but if you're using full RGBA that's 4 bytes per pixel and your image turns into 380K. If you have several of these you're going to run out of memory. Most Android devices are not like PCs - they have rather limited memory. Some have only 16M or 24M to run everything, including the OS and any other apps the user might be running concurrently.
You might try wrapping your routine to load resources with try/catch
While( ! success ) {
try {
// load resources
} catch (OutOfMemoryError E) {
// do something to use less memory and try again
}
}
It may happen if you use high resolution images for mobiles with less resolution and memory. Make use Drawable-hdpi, Drawable-mdpi, Drawable-ldpi folders and place your images with suitable resolution.
FYI.. mostly you may also see this error in emulator when heapSize is too less. Try to increase heap size using AVD manager
This link might help you
Supporting multiple screen resolutions
Look at the below link the out of memory has been discussed in detail.
Android Out of memory issue
For me Thomas's solution has worked in past.
Hope it helps.
you can use this to free memory:
system.gc();
I am trying to put an image on the screen and change it every five seconds. This is not in an Activity class. Sorry if is a stupid question. This is a huge learning curve for me.
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
private Handler mHandler = new Handler();
//stuff
public void doDraw(Canvas canvas) {
int counter = 0;
canvas.drawColor(Color.BLACK);
dot1.doDraw(getResources(), canvas, counter);
mHandler.removeCallbacks(panelDraw);
mHandler.postDelayed(panelDraw, 5000);
}
private Runnable panelDraw = new Runnable() {
public void run() {
}
};
(Panel->)mPanel.doDraw(canvas) in another class calls the doDraw listed in Panel. This is in a loop.
As far as I can tell, the first image isn't stopping for five seconds, and due to other code the cat pic flashes all over the screen. Can I get away with putting nothing in run()? I wanted to put the dot1.doDraw(getResources(), canvas, counter) one in there, but I couldn't/didn't think I could give void run() parameters or Runnable panelDraw resources, canvas, counter parameters.
Any help is appreciated. Let me know if I need to give more code.
A little more about my game: Kitten photos should appear one at random locations on the screen. If one does not click on the current kitty image with an allotted time, the game ends. These pictures last < 1 sec in the end (right now it's more for testing purposes). If you don't click the kitty in that time span, the game ends.
I heard that if you need to do a lot of drawing, you're better off using SurfaceView like in the tutorial I followed very closely: http://www.droidnova.com/2d-tutorial-series-part-v,848.html My Activity class (Scene1) has setContentView(new Panel(this)), so I don't know how to put stuff in the Activity class when it all goes in the Panel class. Like where do I put the ImageView code if the Activity thread doesn't really do much and hands it all off to Panel? Also if I have a ViewThread (public class ViewThread extends Thread) that handles the running:
public void run() {
Canvas canvas = null;
while (mRun1) {
do {
canvas = mHolder.lockCanvas();
if (canvas != null) {
mPanel.doDraw(canvas);
mRun1 = true;
mRun1 = mPanel.get_run();
mHolder.unlockCanvasAndPost(canvas);
}
} while (mRun1 == true);
The dot class has something like:
public void firstRing(Resources res, Canvas canvas, int counter){
Random rand = new Random();
switch(counter) {
case 0: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat1_m);
break;
case 1: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat2_m);
break;
case 2: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat2_m);
break;
case 3: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat2_m);
break;
case 4: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat2_m);
break;
case 5: mBitmap = BitmapFactory.decodeResource(res,
R.drawable.cat1_m);
break;
}
mX = rand.nextInt((int)(Panel.mWidth - mBitmap.getWidth()));
mY = rand.nextInt((int)(Panel.mHeight - mBitmap.getHeight()));
canvas.drawBitmap(mBitmap, mX, mY, null);
and
public void doDraw(Resources res, Canvas canvas, int counter) {
firstRing(res, canvas, counter);
}
Thanks for the info, Barry, although I am a little confused. Do I try ImageView or do I stick with Handler(), although I don't know how to use it and it's not working now? Do I need something in public void run()?
This code should work better than my last answer, but I haven't compiled or tested it. Even if it doesn't work it will point you in the right direction. I assume you have an Activity subclass somewhere so just copy my code into your activity. Your layout should have an <ImageView> tag in it somewhere.
The code below creates a Handler in Activity.onCreate(), which is run in the UI thread, guaranteeing all calls to the Handler will also be run in the UI thread. It then calls showNextKittyImage() to display the first image. showNextKittyImage() sets the Drawable resource id for the ImageView, and then calls Handler.sendEmptyMessageDelayed() to call the handler 5 seconds later. The Handler calls showNextKittyImage() and the cycle repeats.
An ImageView is still preferable to a SurfaceView because it is designed to display images. All you need to do is pass it the Drawable resource id instead of messing with Bitmaps. If you really want or need to do it with a SurfaceView then I cannot help you.
This code will also not make the kittens appear at random locations. Get it working in one location first, then add code to move the ImageView around the screen randomly. If you need help at that point I strongly recommend you post a new question as you've asked A LOT for one posting.
Good luck,
Barry
public class KittyActivity extends Activity {
Handler mHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate your view
setContentView(R.layout.YOUR_LAYOUT);
// Create a handler ON THE UI THREAD
mHandler = new Handler(Message result) {
public void handleMessage(Message result) {
showNextKittyImage();
}
};
// Show the first kitty
showNextKittyImage();
}
public void showNextKittyImage() {
int kitty = getNextKitty();
ImageView iv = findViewById(R.id.YOUR_IMAGE_VIEW_ID);
iv.setImageResource(kitty);
// OPTIONAL: Move the ImageView to a new random location
mHandler.sendEmptyMessageDelayed(0, 5000);
}
private int getNextKitty() {
// Your code to get the next kitty drawable id
}
}
I've got 30+ single bitmaps (320x240 pixels) that I would like to display one after another in full screen on Android devices resulting in an animation. Currently I implemented the animation using an ImageView and a Timer that sets the next frame and then sends a message that will apply the next frame. The resulting frame rate is very low: < 2 fps.
The timer:
animationTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
Drawable frame = getNextFrame();
if (frame != null) {
Message message = animationFrameHandler.obtainMessage(1, frame);
animationFrameHandler.sendMessage(message);
}
}
}, 0, (int) (1000.0d / fps));
The handler:
final Handler animationFrameHandler = new Handler() {
#Override
public void handleMessage(Message message) {
setImageDrawable((Drawable) message.obj);
}
};
Since I want to achieve frame rates up to 30 fps I have to make use of another mechanism and heard of Canvas.drawBitmapMesh() and OpenGL.
If possible I would like to avoid using OpenGL.
Thank you very sharing your experiences!
My now working approach is the following:
Before starting the animation, load every frame into a List<Bitmap>. Important: Call System.gc() if you're getting OutOfMemoryErrors – that really helps loading more bitmaps into the memory. Then have a thread running that posts the next frame to a View instance that then update it's canvas.
Loading the frames and starting the animation
// Loading the frames before starting the animation
List<Bitmap> frames = new ArrayList<Bitmap>();
for (int i = 0; i < 30; i++) {
// Load next frame (e. g. from drawable or assets folder)
frames.add(...);
// Do garbage collection every 3rd frame; really helps loading all frames into memory
if (i %% 3 == 0) {
System.gc();
}
}
// Start animation
frameIndex = 0;
animationThread.start();
Thread that applies the next frame
private final class AnimationThread extends Thread {
#Override
public void run() {
while (!isInterrupted()) {
// Post next frame to be displayed
animationView.postFrame(frames.get(frameIndex));
// Apply next frame (restart if last frame has reached)
frameIndex++;
if (frameIndex >= frames.size()) {
frameIndex = 0;
}
try {
sleep(33); // delay between frames in msec (33 msec mean 30 fps)
} catch (InterruptedException e) {
break;
}
}
}
}
The animation view
class AnimationView extends View {
Bitmap frame = null;
public void postFrame(Bitmap frame) {
Message message = frameHandler.obtainMessage(0, frame);
frameHandler.sendMessage(message);
}
protected final Handler frameHandler = new Handler() {
#Override
public void handleMessage(Message message) {
if (message.obj != null) {
frame = (Bitmap) message.obj;
} else {
frame = null;
}
invalidate();
}
}
#Override
protected void onDraw(Canvas canvas) {
if (frame == null) return;
canvas.drawARGB(0, 0, 0, 0);
canvas.drawBitmap(frame, null, null, null);
}
}
You should look at the FrameAnimation class; http://developer.android.com/guide/topics/graphics/2d-graphics.html#frame-animation to do frame animation with Androids animation.
Though that might still be too slow.
The other alternative if you don't want to use OpenGL ES is to draw to the Canvas as you've mentioned. But just use .drawBitmap, not the drawBitmapMesh. Create a SurfaceView, which has a thread, that thread should draw on your Canvas at whatever interval you want.
It's pretty straightforward, just read the Android docs, the information is all there.
I'll let someone else go into the best way of doing this but one thing that immediately jumps to mind from your post that isn't helping is using TimerTask is a terrible way to do this and is not meant for animation.
Probably won't help with performance, but if those bitmaps are resources you might want to consider using an AnimationDrawable. If not, try to extend Drawable and implement the Animatable interface. Views already have built-in support for animating drawables, no need to use a handler for that.
One way to improve performance might be to match the bit-depth of the drawables to those of your current window. Romain Guy did a keynote on this and animations in general once: http://www.youtube.com/watch?v=duefsFTJXzc