Правильная приостановка потоков onPause() и onResume() с помощью SurfaceView и Thread в Android

Для запуска игр, которые я создал на Android, я использую GamePanel, содержащую методы onDraw() и onTouch(). Я также использовал класс GameThread, который многократно вызывает onDraw() и update().

Моя активность создает экземпляр GamePanel, а моя GamePanel создает экземпляр GameThread.

Мой onPause() просто устанавливает условие в цикле while внутри потока run на false. Мой onResume() просто устанавливал это значение равным true, однако это приводило к ошибке принудительного закрытия (см. редактирование #1) каждый раз, когда я пытался возобновить работу приложения.

Поэтому вместо того, чтобы решить проблему, я просто повторно создал поток,

thread = new GameThread(getHolder(), this);

Это решило часть проблемы, позволив мне свернуть приложение, а затем снова открыть его без проблем. Однако, когда я был в приложении и заблокировал свой телефон, а затем разблокировал его (включив и выключив экран), поток никогда не запускался. Это была бы просто моя игра в замороженном состоянии.

Я знаю, что мое решение проблемы очень странное, и я хотел бы узнать более приемлемый и чистый способ сделать все это.

Вот код (я пропустил несколько строк, которые не кажутся важными):

Игровая активность:

void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    gamePanel = new MainGamePanel(this);
    setContentView(gamePanel);

}
protected void onPause() {
    super.onPause();
    gamePanel.shutDown();

}
protected void onResume() {
    super.onResume();
    gamePanel.startUp();
}

GamePanel

public MainGamePanel(Context context) {
    super(context);
    getHolder().addCallback(this);                  //adding the callback (this) to the surface  holder to intercept events
    thread = new GameThread(getHolder(), this);     // create the game loop thread
    setFocusable(true);                             // make the GamePanel focusable so it can handle events
}
public void startUp() {
    thread = new GameThread(getHolder(), this);
}

public void shutDown() {
    thread.setRunning(false);
}

GameThread

public GameThread(SurfaceHolder surfaceHolder, MainGamePanel gameView) {
    this.surfaceHolder = surfaceHolder;
    this.gameView = gameView;
    this.run = true;
}
public void setRunning(boolean run) {
    this.run = run;
}
public void run() {
    Canvas c;//
    while (run) {
        c = null;
        try {
            c = surfaceHolder.lockCanvas(null);
            synchronized (surfaceHolder) {                  
                gameView.update();
                gameView.onDraw(c);
            }
        } finally {
            if (c != null) {
                surfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

Дайте мне знать, если вам нужна дополнительная информация или как я могу сделать свой вопрос более ясным (также, представил ли я свой код в понятной манере?).

Небольшая помощь будет иметь большое значение. Заранее спасибо, если вы можете дать какие-либо предложения.

--РЕДАКТИРОВАНИЕ №1--

Это была моя ошибка в журнале, когда я получил принудительное закрытие.

12-23 14:01:34.288: E/AndroidRuntime(9484): java.lang.IllegalThreadStateException: Thread already started.

--РЕДАКТИРОВАНИЕ №2--

Я попробовал то, что вы сказали, и изменил свой метод run() на:

@Override
public void run() {
    Canvas c;
    while (true) {
        if (run) {/* run game thread */
            c = null;
            try {
                c = surfaceHolder.lockCanvas(null);
                synchronized (surfaceHolder) {
                    gameView.update();
                    gameView.onDraw(c);
                }
            } finally {
                if (c != null) {
                    surfaceHolder.unlockCanvasAndPost(c);
                }
            }
        } else {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
            }
        }
    }
}

Это сработало, когда я заблокировал свой телефон, но когда я свернул приложение и снова открыл его, у меня появился черный экран, а через несколько секунд появилось всплывающее окно с надписью «MyApp не отвечает. Вы хотите закрыть его?». Я проверил, куда он попал, и, похоже, он добрался до onPause() и ShutDown(), но так и не дошел до onResume();. Кроме того, иногда он даже вызывает update() после вызова onPause() или ShutDown(), что странно. (Получил информацию от LogCat)


person Karl Floersch    schedule 23.12.2012    source источник
comment
Какую ошибку принудительного закрытия вы получили? Можете выложить логкэт?   -  person Luis    schedule 23.12.2012
comment
Два наблюдения: изменчив ли полевой пробег? Поток мог никогда не завершиться, потому что значение run было кэшировано, и когда оно изменилось, оно никогда не считывало новое значение; поэтому он может все еще работать внутри цикла while... Кроме того, вы, вероятно, должны иметь в GameThread while(true){if(run){/*run game thread*/}else{try{Thread.sleep(sleepTime); }поймать(Исключение e){}}}   -  person Luis    schedule 23.12.2012
comment
Ваш поток уже запущен, и вы пытаетесь его запустить, что означает, что поток никогда не видел, когда значение run изменилось с true на false. Вам нужно изменить поле (переменную) run в GameThread на volatile (т.е. public volatile boolean run;)   -  person Luis    schedule 24.12.2012
comment
Я добавил volatile вчера, но, к сожалению, я все еще получаю те же ошибки. Может ли это быть когда-нибудь еще?   -  person Karl Floersch    schedule 25.12.2012
comment
Какие ошибки? IllegalThreadStateException: поток уже запущен? Если это так, не делайте этого: thread = new GameThread(getHolder(), this); так как поток уже запущен. Вместо этого пусть onResume/gamePanel.startUp() реализует следующее: thread.setRunning(true);   -  person Luis    schedule 26.12.2012