【问题标题】:Galaxy Nexus slow with animationsGalaxy Nexus 动画慢
【发布时间】:2013-01-15 01:00:59
【问题描述】:

我已编辑 this code 以将 Rect 实例移出 onDraw 方法。我已经在多台设备上对其进行了测试。

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);
                    }
                }
            }
        }
    }
}

如果您注意到,这里有测量帧速率的代码:

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

我在运行 Android 4.0 和 4.2 的两台 Galaxy Nexus 设备上都看到了这一点,大约为 22-24fps。在我的 HTC Desire 上,运行 Android 2.2,它更像是 60fps。

您还会注意到我没有在 onDraw 方法中分配任何内容。我也没有创建新的 Paint 对象。我真的不明白这是如何运行的,所以,so 在我的 Galaxy Nexus 设备上要慢得多。有很多口吃,球移动得很慢。

有谁知道是否有我可以撤消的设置或在 Galaxy Nexus 上重新绘制的已知问题?这发生在运行 4.0 的 Galaxy Nexus 和运行 4.2 的 Galaxy Nexus 上,所以我不确定它是否特定于操作系统。我已经在开发者选项中关闭了窗口和过渡动画。是否强制进行 2D 加速没有区别。

【问题讨论】:

  • 我遇到了完全相同的问题,我的动画在较旧的 Android 手机(Froyo / Gingerbread)上运行良好,但在我的新 Google Nexus 10 上,它看起来很糟糕。最后,我发现这是因为我在绘制精灵时重新着色了它们,尽管我不确定为什么这个问题没有出现在功率较低的设备上!我拿出了重新画,它现在运行完美。 4.x 一定很奇怪,但如果是 Nexus 10,它可能是巨大的屏幕分辨率。你有可能在你的绘制循环之外以某种方式创建你的 fromRect 和 toRect 吗?
  • 这就是整个程序。所以,我不确定我在哪里可以在绘图循环之外创建 fromRect 和 toRects。你有什么建议吗?
  • 我所做的是在我的主类中创建了一个“Initialise()”方法(所以在你的“Ballbounces”类中)并在这里初始化/创建所有内容。然后从 onCreate() 调用它。我猜你也可以在你的构造函数中创建它?!问题是,您在 onDraw 中创建对象会影响性能,来自 Android:“在 onDraw() 方法中创建绘图对象会显着降低性能,并且会使您的 UI 显得迟缓。”
  • 所以,我想我会做的是用 Rect fromRect1 = new Rect(); 创建前面的矩形。然后只需在 draw 方法中设置它的参数,例如 fromRect1.set(left, top, right, bottom),这样,它只被创建一次,它可能有助于提高性能。希望这会有所帮助
  • @Zippy 感谢您的建议!我实际上尝试将 Rect 实例移动到 onCreate 并在 draw 方法中设置参数,但我仍然看到滞后! sigh 令人沮丧的是,这在此设备上发生。 :(

标签: android android-4.0-ice-cream-sandwich


【解决方案1】:

您是否在应用程序中测试过 android:hardwareAccelerated="true|false" 标志?

Android 开发文档:http://developer.android.com/guide/topics/graphics/hardware-accel.html

你可以在setContentView之前添加:

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

【讨论】:

    【解决方案2】:

    我一直在考虑这个问题,因为我在带有 Surfaceview 的 Nexus 10 上仍然存在性能问题,就像我之前所说的那样,我在去除油漆对象的情况下提高了很多性能,这是一个很长的镜头您只使用了一个,但您可以尝试从 onDraw() 中删除文本绘图部分,看看它是否对您的绘图速度有任何影响。

    除此之外,我认为这确实是一个试图确定问题的案例。

    我注意到,即使我从等式中完全删除了我的 onDraw 和 Logic 更新方法,有时仍需要长达 25 毫秒才能锁定和解锁/发布画布!因此,问题实际上可能不在于 onDraw 方法 - 试一试,从 Run() 方法中注释 onDraw() 并查看您获得的速度(使用日志记录到 Logcat 来查看数据,请记住具有讽刺意味的是,在屏幕上显示帧/时间计数可能会影响您正在测量的内容)。 :-)

    【讨论】:

      【解决方案3】:

      你的 targetSdk 设置是什么?

      如果 targetSdk 设置为 8 (2.2),但您在 4.x 设备 (15+) 上运行它,则 4.x 设备将以兼容模式运行,这意味着它将虚拟化所有调用,因此它们返回的结果与在版本 8 设备上运行时完全相同。

      这种虚拟化可能会解释一些缓慢的原因。尝试将 targetSdk 更改为 17(适用于您的 4.2 设备),看看是否有所不同。

      【讨论】:

      • 我正在使用:&lt;uses-sdk android:targetSdkVersion="17" android:minSdkVersion="14"/&gt;
      • 它仍然很慢(抱歉,忘了提及)。我认为有趣的是,大多数时候,它的运行速度约为 30fps。偶尔,几秒钟,它会以大约 60fps 的速度运行。太奇怪了!
      【解决方案4】:

      我在 Kindle Fire、Sony Xperia Z 和 Samsung S4(均使用 android 4.2)上遇到了同样的情况。

      修复方法是:在 App Manifest 文件中删除 "android:supportsRtl="true""。

      希望它能节省您的时间。在得到它之前,我花了 4 个小时进行测试和合并。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-22
        • 2013-11-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多