java - Android SurfaceView UI 线程不工作

标签 java android multithreading android-canvas

我的 Android 应用程序出现问题 (Android ANR keyDispatchingTimedOut)。我知道背后的原因,但我无法解决问题。我的 UI 线程可能重载,我想知道如何将昂贵的代码/方法移至后台线程。我还想知道,什么样的图形/更新方法可以移动到后台线程?

这是我的 UI 线程:

Canvas canvas;
Log.d(TAG, "Starting game loop");

long beginTime; // The time when the cycle begun
long timeDiff; // The time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // Number of frames being skipped

sleepTime = 0;

while (running) {
    canvas = null;

    // Try locking the canvas
    try {
        canvas = this.surfaceHolder.lockCanvas();
        if (canvas != null) {
            synchronized (surfaceHolder) {
                beginTime = System.currentTimeMillis();
                framesSkipped = 0; // resetting the frames skipped
                // Update game state here!
                this.gameView.update();
                // Render state to the screen
                // Draws the canvas on the panel
                this.gameView.render(canvas);
                // Calculate how long time the cycle took
                timeDiff = System.currentTimeMillis() - beginTime;
                // Calculate sleep time
                sleepTime = (int) (FRAME_PERIOD - timeDiff);

                if (sleepTime > 0) {
                    try {
                        // Send the thread to sleep for a short period,
                        // very useful for battery saving
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                    }
                }

                while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
                    // Need to catch up by updating without rendering
                    // Update game state here!
                    this.gameView.update();

                    // Add frame period to check if in next frame
                    sleepTime += FRAME_PERIOD;
                    framesSkipped++;
                }
            }
        }
    } finally {
        // In case of an exception the surface is not left in
        // an inconsistent state
        if (canvas != null) {
            surfaceHolder.unlockCanvasAndPost(canvas);
        }
    } // End finally
}

这是两个方法,update() 和 render()

public void update() {

// Gets the start level if it isn't initialized (when you
// enter the game for the first time)
if (startLvl == 0) {
    startLvl = ((SingleGameActivity)getContext()).getStartLvl();
    currentLvl = startLvl;
}

if (playing) {
    // Update the life icons
    updatePlayerLives();
    updateComputerLives();

    // Checks if you have won or lost
    outOfBounds();

    // Update the position of the ball, player and computer including
    // collision detection and reaction.
    ball.moveWithCollisionDetection(box, player, computer);
    player.moveWithCollisionDetection(box);
    computer.setDirectionWithAI(ball);
    computer.moveWithCollisionDetection(box);

} else {
    // Create new objects
    newObjects();

    if (newRound) {
        playing = true;
        newRound = false;
    }
  }
}

// //////////////////////////////////////////////////////////////////////////////////
// The render() method renders the UI graphics on the screen
public void render(Canvas canvas) {
super.onDraw(canvas);

if (playing) {

    // Draw components
    box.draw(canvas);

    // Draw booster/s
    if (racketTouches > 4) {
        if (b1.isUsed()) {
            b1 = new Booster();

        }
        canvas.drawBitmap(b1.booster, b1.boosterX, b1.boosterY, null);

    }
    if (racketTouches > 14) {
        if (b2.isUsed()) {
            b2 = new Booster();

        }
        canvas.drawBitmap(b2.booster, b2.boosterX, b2.boosterY, null);

    }

    if (racketTouches > 24) {
        if (b3.isUsed()) {
            b3 = new Booster();

        }
        canvas.drawBitmap(b3.booster, b3.boosterX, b3.boosterY, null);
    }           

    // Draw rackets and ball
    player.draw(canvas);
    computer.draw(canvas);
    ball.draw(canvas);

 } else {
    // Draw components
    box.draw(canvas);
    player.draw(canvas);
    computer.draw(canvas);
    ball.draw(canvas);
   }
}

方法是否重载?如果是这样,我可以将什么移动到后台线程?又如何?

仅供引用:我正在创建一款包含助推器的复古乒乓球/网球游戏。

最佳答案

您永远不想在 UI 线程上执行昂贵的任务,例如绘图和动画。有两种方法可以利用工作线程...

具有可运行的线程(这可以在绘图表面内创建,绘图表面必须实现 Runnable):

new Thread(new Runnable() {
            public void run() {
                update();
                render();
            }
        }).start();

在单独的类中扩展线程:

class myThread extends Thread {
    Context context;
    //Everything that you currently have in your UI thread should be in here...

    /* Constructor for thread. Pass useful things like context, drawing surface, etc */
    public myThread(Context context) {
        this.context = context;
    }

    public void setRunning(Boolean running) {
        this.running = running;
    }

    @Override
    public void run() {

    Canvas canvas;

    Log.d(TAG, "Starting game loop");

    long beginTime; // The time when the cycle begun
    long timeDiff; // The time it took for the cycle to execute
    int sleepTime; // ms to sleep (<0 if we're behind)
    int framesSkipped; // Number of frames being skipped

    sleepTime = 0;

    while (running) {
        canvas = null;

        // Try locking the canvas
        try {
            canvas = this.surfaceHolder.lockCanvas();
            if (canvas != null) {
                synchronized (surfaceHolder) {
                    beginTime = System.currentTimeMillis();
                    framesSkipped = 0; // resetting the frames skipped
                    // Update game state here!
                    this.gameView.update();
                    // Render state to the screen
                    // Draws the canvas on the panel
                    this.gameView.render(canvas);
                    // Calculate how long time the cycle took
                    timeDiff = System.currentTimeMillis() - beginTime;
                    // Calculate sleep time
                    sleepTime = (int) (FRAME_PERIOD - timeDiff);

                    if (sleepTime > 0) {
                        try {
                            // Send the thread to sleep for a short period,
                            // very useful for battery saving
                            Thread.sleep(sleepTime);
                        } catch (InterruptedException e) {

                        }
                    }

                    while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
                        // Need to catch up by updating without rendering
                        // Update game state here!
                        this.gameView.update();

                        // Add frame period to check if in next frame
                        sleepTime += FRAME_PERIOD;
                        framesSkipped++;
                    }
                }
            }
            } finally {
                // In case of an exception the surface is not left in
                // an inconsistent state
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            } // End finally
        }
    }         
}

当您创建绘图表面时,分配线程:

Thread thread;

@Override
public void surfaceCreated(SurfaceHolder holder) {
    thread = new myThread(getContext());

    thread.setRunning(true);
    thread.start();
}

差不多就是这样 - 请注意,您可能必须通过第二个示例中的构造函数将一些其他内容传递给线程,以便它能够与您的游戏一起使用,例如您正在绘制的表面和SurfaceHolder,但这就是想法。

祝您游戏愉快!

关于java - Android SurfaceView UI 线程不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25044050/

相关文章:

java - 从 Hibernate 中的集合中删除项目而不加载实体

java - Controller 中的 Spring 重定向显示错误

java - 错误: unreported exception InterruptedException; must be caught or declared to be thrown (calling AsyncTask)

java - 使用我自己在 OpenCV 中的公式将 RGB 转换为灰度

c++ - 如何使我的组件实体系统线程安全?

java - 如何设置Jtable的列宽等于复选框

android - 字符按我点击的顺序出现

android - 是否可以在模拟器中使用 phoneGap 显示动画 gif 图像

python - 如何使用pyserial清除热插拔缓冲区中的先前数据

c++ - 使用 spsc_queue 避免在单一生产者单一消费者程序中忙于等待