как приостановить и возобновить поток surfaceView
У меня есть настройка surfaceView и работает, но когда я возобновляю его, я получаю сообщение об ошибке, что поток уже запущен. Каков правильный способ обработки, когда приложение переходит в фоновый режим, а затем возвращается на передний план? Я возился и сумел заставить приложение вернуться без сбоев... но surfaceView больше ничего не рисует. Мой код:
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e("sys","surfaceCreated was called.");
if(systemState==BACKGROUND){
thread.setRunning(true);
}
else {
thread.setRunning(true);
thread.start();
Log.e("sys","started thread");
systemState=READY;
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e("sys","surfaceDestroyed was called.");
thread.setRunning(false);
systemState=BACKGROUND;
}
8 ответов
самое простое решение-просто убить и перезапустить поток. Create methods resume () - создает объект thread и запускает его - и pause () - убивает поток (см. Пример Lunarlander) - в вашем классе SurfaceView и вызывает их из surfaceCreated и surfaceDestroyed для запуска и остановки потока.
теперь в действии, которое запускает SurfaceView, вам также нужно будет вызвать методы resume () и pause() в SurfaceView из onResume () действия (или фрагмента) и onPause (). Это не элегантное решение, но оно сработает.
эта ошибка, похоже, связана с ошибкой lunar lander, которая довольно известна (сделайте поиск Google на ней). После всего этого времени и после нескольких выпусков версии android ошибка все еще существует и никто не потрудился обновить его. я обнаружил, что это работает с наименьшим беспорядком кода:
public void surfaceCreated(SurfaceHolder holder) {
if (thread.getState==Thread.State.TERMINATED) {
thread = new MainThread(getHolder(),this);
}
thread.setRunning(true);
thread.start();
}
public void surfaceCreated(SurfaceHolder holder) {
if (!_thread.isAlive()) {
_thread = new MyThread(this, contxt);
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
лучший способ, который я нашел, - переопределить метод onResume действия, управляющего представлением поверхности, так что с помощью метода он повторно создает SurfaceView, а затем устанавливает его с помощью setContentView. Проблема с этим подходом заключается в том, что вам нужно перезагрузить любое состояние, о котором заботился SurfaceView.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyCustomSurfaceView(this));
}
@Override
protected void onResume() {
super.onResume();
setContentView(new MyCustomSurfaceView(this));
}
попытался прокомментировать принятый ответ выше, но не мог, в этом. Я не думаю, что вы должны вызывать свои методы Start/stop thread как из SurfaceView, так и из Activity. Это приведет к запуску / остановке потока дважды, и вы не можете запустить поток более одного раза. Просто вызовите свои методы из onPause и onResume активности. Они вызываются при выходе и повторном входе в приложение, чтобы убедиться, что ваши состояния обрабатываются должным образом. surfaceDestroyed не всегда звонил, что на какое-то время сбило меня с толку.
Если вы используете этот метод, обязательно проверьте правильность поверхности в коде запуска перед работой с холстом, потому что действие запустит поток в onResume до того, как поверхность будет доступна.
while (_run) {
if (_surfaceHolder.getSurface().isValid()) {
...
}
} //end _run
это то, что я использовал. Приложение не аварийно завершает работу.
Класс Вид:
holder.addCallback(new Callback() {
public void surfaceDestroyed(SurfaceHolder holder) {
gameLoopThread.setRunning(false);
gameLoopThread.stop();
}
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
в GameLoopThread:
private boolean running = false;
public void setRunning(boolean run) {
running = run;
}
@Override
public void run() {
long ticksPs=1000/FPS;
long startTime;
long sleepTime;
while(running){
Canvas c = null;
startTime=System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime=ticksPs-(System.currentTimeMillis()-startTime);
try{
if(sleepTime>0){
sleep(sleepTime);
}
else
sleep(10);
} catch(Exception e){}
}
}
Я надеюсь, что это поможет.
вы должны использовать методы Activities onPause() и onResume ().
во-первых, в surfaceCreated(), запустить поток. Кроме того, в onResume () убедитесь, что поток еще не запущен (сохраните переменную внутри потока или что-то еще). Затем, если он не работает, установите его как запущенный снова. в onPause () приостановите поток. В surfaceDestroyed, приостановите нить снова.
другое решение для этой проблемы. К сожалению, я не понимаю, почему это работает-это вышло случайно. Но это хорошо работает для меня, и это легко реализовать: нет переопределения Activity
' s onPause()
, onResume()
, onStart()
, onStop()
, ни написание специальных методов потока (например,resume()
, pause()
) не требуется.
специальное требование-поместить все изменяющиеся переменные во что-то другое, кроме класса потока рендеринга.
основные моменты для добавления класс render-thread:
class RefresherThread extends Thread {
static SurfaceHolder threadSurfaceHolder;
static YourAppViewClass threadView;
static boolean running;
public void run (){
while(running){
//your amazing draw/logic cycle goes here
}
}
}
теперь, важные вещи о YourAppViewClass
:
class YourAppViewClass extends SurfaceView implements SurfaceHolder.Callback {
static RefresherThread surfaceThread;
public YourAppViewClass(Activity inpParentActivity) {
getHolder().addCallback(this);
RefresherThread.threadSurfaceHolder = getHolder();
RefresherThread.threadView = this;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceThread = new RefresherThread();
surfaceThread.running=true;
surfaceThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
surfaceThread.running=false;
try {
surfaceThread.join();
} catch (InterruptedException e) {
}
}
}
два блока кода выше-это не полностью написанные классы, а просто представление о том, какие команды, в которых необходимы методы. Также обратите внимание, что каждый возврат в приложение вызывает surfaceChanged()
.
извините за такой пространственный ответ. Надеюсь, он будет работать правильно и поможет.