title: Galaxy Nexus slow with animations
body: <p>I've edited <a href="https://stackoverflow.com/questions/4938822/how-can-i-use-the-animation-framework-inside-the-canvas/4946893">this code</a> to move the Rect instantiations out of the onDraw method. I've tested it on several devices.</p>
  
  <pre><code>public class BallBouncesActivity extends Activity {
      BallBounces ball;
  
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          ball = new BallBounces(this);
          setContentView(ball);
      }
  }
  
  
  class BallBounces extends SurfaceView implements SurfaceHolder.Callback {
      GameThread thread;
      int screenW; //Device's screen width.
      int screenH; //Devices's screen height.
      int ballX; //Ball x position.
      int ballY; //Ball y position.
      int initialY ;
      float dY; //Ball vertical speed.
      int ballW;
      int ballH;
      int bgrW;
      int bgrH;
      int angle;
      int bgrScroll;
      int dBgrY; //Background scroll speed.
      float acc;
      Bitmap ball, bgr, bgrReverse;
      boolean reverseBackroundFirst;
      boolean ballFingerMove;
  
      Rect toRect1, fromRect1;
      Rect toRect2, fromRect2;
  
      //Measure frames per second.
      long now;
      int framesCount=0;
      int framesCountAvg=0;
      long framesTimer=0;
      Paint fpsPaint=new Paint();
  
      //Frame speed
      long timeNow;
      long timePrev = 0;
      long timePrevFrame = 0;
      long timeDelta;
  
  
      public BallBounces(Context context) {
          super(context);
          ball = BitmapFactory.decodeResource(getResources(), R.drawable.rocket); //Load a ball image.
          bgr = BitmapFactory.decodeResource(getResources(), R.drawable.sky_bgr); //Load a background.
          ballW = ball.getWidth();
          ballH = ball.getHeight();
  
          toRect1 = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
          fromRect1 = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
          toRect2 = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
          fromRect2 = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
  
          //Create a flag for the onDraw method to alternate background with its mirror image.
          reverseBackroundFirst = false;
  
          //Initialise animation variables.
          acc = 0.2f; //Acceleration
          dY = 0; //vertical speed
          initialY = 100; //Initial vertical position
          angle = 0; //Start value for the rotation angle
          bgrScroll = 0;  //Background scroll position
          dBgrY = 1; //Scrolling background speed
  
          fpsPaint.setTextSize(30);
  
          //Set thread
          getHolder().addCallback(this);
  
          setFocusable(true);
      }
  
      @Override
      public void onSizeChanged (int w, int h, int oldw, int oldh) {
          super.onSizeChanged(w, h, oldw, oldh);
          //This event-method provides the real dimensions of this custom view.
          screenW = w;
          screenH = h;
  
          bgr = Bitmap.createScaledBitmap(bgr, w, h, true); //Scale background to fit the screen.
          bgrW = bgr.getWidth();
          bgrH = bgr.getHeight();
  
          //Create a mirror image of the background (horizontal flip) - for a more circular background.
          Matrix matrix = new Matrix();  //Like a frame or mould for an image.
          matrix.setScale(-1, 1); //Horizontal mirror effect.
          bgrReverse = Bitmap.createBitmap(bgr, 0, 0, bgrW, bgrH, matrix, true); //Create a new mirrored bitmap by applying the matrix.
  
          ballX = (int) (screenW /2) - (ballW / 2) ; //Centre ball X into the centre of the screen.
          ballY = -50; //Centre ball height above the screen.
      }
  
      //***************************************
      //*************  TOUCH  *****************
      //***************************************
      @Override
      public synchronized boolean onTouchEvent(MotionEvent ev) {
  
          switch (ev.getAction()) {
              case MotionEvent.ACTION_DOWN: {
                  ballX = (int) ev.getX() - ballW/2;
                  ballY = (int) ev.getY() - ballH/2;
  
                  ballFingerMove = true;
                  break;
              }
  
              case MotionEvent.ACTION_MOVE: {
                  ballX = (int) ev.getX() - ballW/2;
                  ballY = (int) ev.getY() - ballH/2;
  
                  break;
              }
  
              case MotionEvent.ACTION_UP:
                  ballFingerMove = false;
                  dY = 0;
                  break;
              }
          return true;
      }
  
      @Override
      public void onDraw(Canvas canvas) {
          super.onDraw(canvas);
  
          //Draw scrolling background.
          fromRect1.set(0, 0, bgrW - bgrScroll, bgrH);
          toRect1.set(bgrScroll, 0, bgrW, bgrH);
  
          fromRect2.set(bgrW - bgrScroll, 0, bgrW, bgrH);
          toRect2.set(0, 0, bgrScroll, bgrH);
  
  //        Rect fromRect1 = new Rect(0, 0, bgrW - bgrScroll, bgrH);
  //        Rect toRect1 = new Rect(bgrScroll, 0, bgrW, bgrH);
  //
  //        Rect fromRect2 = new Rect(bgrW - bgrScroll, 0, bgrW, bgrH);
  //        Rect toRect2 = new Rect(0, 0, bgrScroll, bgrH);
  
          if (!reverseBackroundFirst) {
              canvas.drawBitmap(bgr, fromRect1, toRect1, null);
              canvas.drawBitmap(bgrReverse, fromRect2, toRect2, null);
          }
          else{
              canvas.drawBitmap(bgr, fromRect2, toRect2, null);
              canvas.drawBitmap(bgrReverse, fromRect1, toRect1, null);
          }
  
          //Next value for the background's position.
          if ( (bgrScroll += dBgrY) &gt;= bgrW) {
              bgrScroll = 0;
              reverseBackroundFirst = !reverseBackroundFirst;
          }
  
          //Compute roughly the ball's speed and location.
          if (!ballFingerMove) {
              ballY += (int) dY; //Increase or decrease vertical position.
              if (ballY &gt; (screenH - ballH)) {
                  dY=(-1)*dY; //Reverse speed when bottom hit.
              }
              dY+= acc; //Increase or decrease speed.
          }
  
          //Increase rotating angle
          if (angle++ &gt;360)
              angle =0;
  
          //DRAW BALL
          //Rotate method one
          /*
          Matrix matrix = new Matrix();
          matrix.postRotate(angle, (ballW / 2), (ballH / 2)); //Rotate it.
          matrix.postTranslate(ballX, ballY); //Move it into x, y position.
          canvas.drawBitmap(ball, matrix, null); //Draw the ball with applied matrix.
  
          */// Rotate method two
  
          canvas.save(); //Save the position of the canvas matrix.
          canvas.rotate(angle, ballX + (ballW / 2), ballY + (ballH / 2)); //Rotate the canvas matrix.
          canvas.drawBitmap(ball, ballX, ballY, null); //Draw the ball by applying the canvas rotated matrix.
          canvas.restore(); //Rotate the canvas matrix back to its saved position - only the ball bitmap was rotated not all canvas.
  
          //*/
  
          //Measure frame rate (unit: frames per second).
           now=System.currentTimeMillis();
           canvas.drawText(framesCountAvg+" fps", 40, 70, fpsPaint);
           framesCount++;
           if(now-framesTimer&gt;1000) {
                   framesTimer=now;
                   framesCountAvg=framesCount;
                   framesCount=0;
           }
      }
  
      @Override
      public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
      }
  
      @Override
      public void surfaceCreated(SurfaceHolder holder) {
          thread = new GameThread(getHolder(), this);
          thread.setRunning(true);
          thread.start();
      }
  
      @Override
      public void surfaceDestroyed(SurfaceHolder holder) {
          boolean retry = true;
          thread.setRunning(false);
          while (retry) {
              try {
                  thread.join();
                  retry = false;
              } catch (InterruptedException e) {
  
              }
          }
      }
  
  
      class GameThread extends Thread {
          private SurfaceHolder surfaceHolder;
          private BallBounces gameView;
          private boolean run = false;
  
          public GameThread(SurfaceHolder surfaceHolder, BallBounces gameView) {
              this.surfaceHolder = surfaceHolder;
              this.gameView = gameView;
          }
  
          public void setRunning(boolean run) {
              this.run = run;
          }
  
          public SurfaceHolder getSurfaceHolder() {
              return surfaceHolder;
          }
  
          @Override
          public void run() {
              Canvas c;
              while (run) {
                  c = null;
  
                  //limit frame rate to max 60fps
                  timeNow = System.currentTimeMillis();
                  timeDelta = timeNow - timePrevFrame;
                  if ( timeDelta &lt; 16) {
                      try {
                          Thread.sleep(16 - timeDelta);
                      }
                      catch(InterruptedException e) {
  
                      }
                  }
                  timePrevFrame = System.currentTimeMillis();
  
                  try {
                      c = surfaceHolder.lockCanvas(null);
                      synchronized (surfaceHolder) {
                         //call methods to draw and process next fame
                          gameView.onDraw(c);
                      }
                  } finally {
                      if (c != null) {
                          surfaceHolder.unlockCanvasAndPost(c);
                      }
                  }
              }
          }
      }
  }
  </code></pre>
  
  <p>If you notice, there's code to measure the frame rate:</p>
  
  <pre><code>     now=System.currentTimeMillis();
       canvas.drawText(framesCountAvg+" fps", 40, 70, fpsPaint);
       framesCount++;
       if(now-framesTimer&gt;1000) {
               framesTimer=now;
               framesCountAvg=framesCount;
               framesCount=0;
       }
  </code></pre>
  
  <p>I'm seeing that on both of my Galaxy Nexus devices, running Android 4.0 and 4.2, it's around 22-24fps. On my HTC Desire, running Android 2.2, it's more like 60fps.</p>
  
  <p>You'll also notice that I'm not allocating anything in the <code>onDraw</code> method. I'm not creating new <code>Paint</code> objects either. I really don't see how this is running so, so, <em>so</em> much slower on the my Galaxy Nexus devices. There's a lot of stuttering and the ball moves very slowly. </p>
  
  <p>Does anyone know whether there is a setting I can undo or a known issue with re-drawing on the Galaxy Nexus? This is happening to my Galaxy Nexus that's running 4.0 <em>and</em> the one that's running 4.2, so I'm not sure it's OS-specific. I have turned off the window and transition animations in Developer Options. It doesn't make a difference whether I force 2D acceleration.</p>