简体   繁体   中英

Galaxy Nexus slow with animations

I've edited this code to move the Rect instantiations out of the onDraw method. I've tested it on several devices.

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) >= 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 > (screenH - ballH)) {
                dY=(-1)*dY; //Reverse speed when bottom hit.
            }
            dY+= acc; //Increase or decrease speed.
        }

        //Increase rotating angle
        if (angle++ >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>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 < 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);
                    }
                }
            }
        }
    }
}

If you notice, there's code to measure the frame rate:

     now=System.currentTimeMillis();
     canvas.drawText(framesCountAvg+" fps", 40, 70, fpsPaint);
     framesCount++;
     if(now-framesTimer>1000) {
             framesTimer=now;
             framesCountAvg=framesCount;
             framesCount=0;
     }

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.

You'll also notice that I'm not allocating anything in the onDraw method. I'm not creating new Paint objects either. I really don't see how this is running so, so, so much slower on the my Galaxy Nexus devices. There's a lot of stuttering and the ball moves very slowly.

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 and 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.

Have you tested android:hardwareAccelerated="true|false" flag in application?

Android Dev Doc: http://developer.android.com/guide/topics/graphics/hardware-accel.html

You can add before setContentView:

if (android.os.Build.VERSION.SDK_INT >= 11) {
   getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}

What's your targetSdk set to?

If targetSdk is set to 8 (2.2), but you're running it on a 4.x device (15+), the 4.x device will run in compatibility mode, meaning that it will virtualize all the calls so that they return exactly the same as if they're running on a version 8 device.

This virtualization might explain some of the slowness. Try changing targetSdk to 17 (for your 4.2 device) and see if it makes a difference.

I've been thinking about this, as I'm still having performance issues on my Nexus 10 with surfaceview, like I said previously, I increased my performance a lot my removing the use of paint objects, it's a long shot as you are only using one, but you could try to remove the text drawing section from your onDraw() just to see if it makes any difference to your drawing speed.

Other than that, I think it's really a case of trying to pin down the problem.

I've noticed that even if I remove my onDraw and Logic updating methods completely from the equation, it's still taking sometimes up to 25ms just to lock and unlock/post the canvas! So it might be that the problem actually doesn't lie in onDraw method - give it a go, comment onDraw() out from your Run() method and see what speeds you get (Use logging to Logcat to see the figures, remember that ironically, displaying a frame / time count on the screen could affect the very thing you're measuring). :-)

I has experienced the same thing on Kindle Fire, Sony Xperia Z and Samsung S4 (all with android 4.2).

The fix is: at App Manifest file remove "android:supportsRtl="true"".

Hope it will save your time. I spend 4 hours on tests and merging before i got it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM