Поток пользовательского интерфейса Android заблокирован в новом действии

Я работаю над небольшой игрой для Android, которая обрабатывает рисунок на холсте в рабочем потоке. После завершения игры этот поток должен начать следующее действие, но каким-то образом поток пользовательского интерфейса блокируется запуском новой функции.

Это мои игры Main-Loop:

while(true)
    // Perform Ticks until the player makes a mistake
    c = _surfaceHolder.lockCanvas();
    if ((toDraw = rh.performTick()) == null) {
         lose();
         break;
     }
     _surfaceHolder.unlockCanvasAndPost(draw(toDraw,c));
}

Функция performTick() обрабатывает логику игры и возвращает null по окончании игры. draw()выполняет рендеринг. Обе функции работают идеально - проблема в lose(), которая ничего не делает, кроме как запускает новую активность:

    protected void lose() {
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
                Intent intent = new Intent(_context, MainMenu.class);
                _context.startActivity(intent);
            }
        });
    }

Когда игра заканчивается, новый Activiy (MainMenu) запускается нормально, но не реагирует ни на какие сенсорные события. Выходных данных LogCat нет, и исключений не возникает. Хуже того, lose() работает нормально, когда вызывается ДО начала основного цикла.

Я работаю над этим уже несколько дней. Пробовал запускать Activity прямо из рабочего Thread (без использования Handler в lose()) и часами копался в интернете - ничего не помогает. Похоже, поток пользовательского интерфейса каким-то образом заблокирован или попал в бесконечный цикл, и я понятия не имею, как это исправить.

Все отлажено на устройстве Nexus 4.

Кстати: MainMenu.onCreate() работает нормально, когда запускается другим способом.

Пожалуйста, помогите, MA3o


person MA3o    schedule 02.06.2014    source источник


Ответы (2)


Вы не разблокируете холст, когда оператор if истинен. Перерыв выведет вас из тишины, оставив холст запертым.

while(true)
    // Perform Ticks until the player makes a mistake
    c = _surfaceHolder.lockCanvas();
    if ((toDraw = rh.performTick()) == null) {
         lose();
         break;
     }

     // this line won't be executed whether the if statement above is true
     _surfaceHolder.unlockCanvasAndPost(draw(toDraw,c));
}

Вы также должны разблокировать оператор if (до или после loss()).

person kupsef    schedule 02.06.2014
comment
Это просто... Работает нормально, большое спасибо! - person MA3o; 03.06.2014

Вместо этого, если While(true), вы могли бы иметь While(myVar), где myVar — логическое значение, которое истинно, пока игра должна работать, и установлено в false, когда вы вызываете lose(). В вашем случае вы продолжали рисовать, хотя вам это не нужно.

person orrett3    schedule 02.06.2014
comment
Он не будет продолжать рисовать, потому что разрыв внутри оператора if отменяет время. - person kupsef; 03.06.2014
comment
Да, я комментировал то, что Ma3o написал не ваше, ваше совершенно верно, мое решение будет запускать '_surfaceHolder.unlockCanvasAndPost(draw(toDraw,c));' еще раз после того, как пользователь уже проиграл, что, вероятно, не то, что он хотел. - person orrett3; 03.06.2014
comment
Даже если бы это также решило проблему (потому что цикл while завершится и снова разблокирует холст), сам ответ неверен. Ключевое слово break;используется для немедленного завершения цикла, независимо от его нормального поведения, поэтому цикл завершается, когда был вызван lose(), и программа прекращает рисование. - person MA3o; 03.06.2014