質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.99%

AndroidのSurfaceViewの描画が遅い

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 2,239

Rio12882

score 25

現在アンドロイドアプリで円が放射運動をするSurfaceViewの開発をしたのですが、FPSが低すぎて、ボールが滑らかに運動しているように見えなくて困っています。

あまりAndroidについて知らないので、基本的なところの理解不足が原因かもしれませんがなにか改善方法があればご教示ください。
よろしくお願いいたします。

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private Paint mPaint = null;
    private Canvas mCanvas = null;
    static final long FPS = 20;
    static final long FRAME_TIME = 1000 / FPS;
    static final int BALL_R = 10;
    SurfaceHolder surfaceHolder;
    Thread thread;
    DisplayMetrics dm = Resources.getSystem().getDisplayMetrics();
    int width = dm.widthPixels;
    int height = dm.heightPixels;
    float cx = 0, cy = 0;
    int screen_width, screen_height;
    float g = (float) 9.8;
    int temp1, temp2;
    double velocity = (width-60)/(35*Math.cos(Math.PI*4/9));
    float vcos = (float) (velocity*Math.cos(Math.PI*4/9));
    float vsin = (float) (velocity*Math.sin(Math.PI*4/9));



    public MySurfaceView(Context context){
        super(context);
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
    }
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        thread = new Thread(this);
        thread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        thread = null;
    }

    @Override
    public void run() {
        Canvas canvas = null;
        Paint paint = new Paint();
        Paint bgPaint = new Paint();
        // Background
        bgPaint.setStyle(Paint.Style.FILL);
        bgPaint.setColor(Color.WHITE);
        // Ball
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.BLUE);

        long startTime = System.currentTimeMillis();
        float time = 0;
        float cvy;
        int temp1, temp2;

        temp1 = (int) (velocity * 10000);
        temp2 = temp1 % 10;
        temp1 -= temp2;
        velocity = ((float)temp1)/10000;
        temp1 = (int) (vcos * 10000);
        temp2 = temp1 % 10;
        temp1 -= temp2;
        vcos = ((float)temp1)/10000;
        temp1 = (int) (vsin * 10000);
        temp2 = temp1 % 10;
        temp1 -= temp2;
        vsin = ((float)temp1)/10000;

        while(thread != null){
            time = (System.currentTimeMillis()-startTime)/1000;
            temp1 = (int) (time * 10000);
            temp2 = temp1 % 10;
            temp1 -= temp2;
            time = ((float)temp1)/10000;
            cvy = g*time - vsin;
            cx = vcos*time;
            cy = cvy*time/2-vsin*time/2;

            try{
                canvas = surfaceHolder.lockCanvas();
                canvas.drawCircle(
                        cx+BALL_R, cy+height-200, BALL_R,
                        paint);

            }
            catch(Exception e){}
            finally {
                surfaceHolder.unlockCanvasAndPost(canvas);
            }
            canvas = surfaceHolder.lockCanvas();
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
            surfaceHolder.unlockCanvasAndPost(canvas);


            long end = System.currentTimeMillis();
            Log.d(getClass().getName(), "measure: " + (end-startTime));

        }


    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • abs123

    2016/10/28 10:36

    動作確認を行っている環境を追記してください。

    キャンセル

回答 1

checkベストアンサー

0

surfaceHolder.unlockCanvasAndPost(canvas);


上記の処理を一回のループで2回読んでいるから重いのかもしれないです。
下記にループ内の処理を変えてみてはどうですかね?

while(thread != null){
            time = (System.currentTimeMillis()-startTime)/1000;
            temp1 = (int) (time * 10000);
            temp2 = temp1 % 10;
            temp1 -= temp2;
            time = ((float)temp1)/10000;
            cvy = g*time - vsin;
            cx = vcos*time;
            cy = cvy*time/2-vsin*time/2;

            try{
                canvas = surfaceHolder.lockCanvas();
                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
                canvas.drawCircle(
                        cx+BALL_R, cy+height-200, BALL_R,
                        paint);

            }
            catch(Exception e){}
            finally {
                surfaceHolder.unlockCanvasAndPost(canvas);
            }

            long end = System.currentTimeMillis();
            Log.d(getClass().getName(), "measure: " + (end-startTime));

        }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/28 04:40

    全く変わらないです。
    自分は、cx、cyの計算がfloatの計算なので時間がかかるのかと思いtemp1やtemp2を用いて丸めて計算させたのですが変わりませんでした。
    また、cx+=1のように簡単な計算にして描画するとおそらく通常の速さで描画されるのも確認しました。

    キャンセル

  • 2016/10/28 08:07 編集

    これ処理が重いのではなく、1フレーム辺りのx座標の計算に問題がありそうです。ループ処理内にx座標の値のログを入れて確認してみてください。何ループも同じ値が表示されませんか。

    演算処理は基本そんな重い処理ではなく重いのは描画処理なので、あまり考慮する必要はないはずです。

    キャンセル

  • 2016/10/28 14:53

    なんループも同じxの値が表示されていました。
    約20millisごとに更新されている感じでした。30ループごとに値が変更されていてその時間1.6秒程度だったので、まさにこれが問題のようです。
    どのようにしたら改善されるのでしょうか。
    System.currentTimeMillisを使わないで、一回のループあたりの時間を考慮して
    time += 0.03
    としたところ、期待通りの動きができました。
    端末や環境によって1ループの実行時間が違うと思うので、なにか一般性のある改善方法があったら教えていただきたいです。

    キャンセル

  • 2016/10/28 20:20

    >>端末や環境によって1ループの実行時間が違うと思うので、なにか一般性のある改善方法があったら教えていただきたいです。

    Thread.sleep()をループ処理の最後に実装して指定したFPSで描画できる様に調整するのが一般的だと思います。処理落ちしない限り端末差がでなくなります。

    そんな感じでやってみてダメだったらまた質問してはどうですか。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる