Как приостановить и возобновить поток surfaceView

У меня есть настройка и запуск surfaceView, но когда я его возобновляю, я получаю сообщение об ошибке, когда поток уже запущен. Каким образом можно обращаться, когда приложение переходит на задний план, а затем обратно на передний план? Ive возился вокруг и сумел заставить приложение вернуться без сбоев... но 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;
    }

Ответы

Ответ 1

Простое решение - просто убить и перезапустить поток. Создание методов resume() - создает объект потока и запускает его - и pause() - убивает поток (см. Пример Lunarlander) - в вашем классе SurfaceView и вызывайте их из surfaceCreated и surfaceDestroyed, чтобы запустить и остановить поток.

Теперь в Activity, который запускает SurfaceView, вам также необходимо вызвать методы resume() и pause() в SurfaceView из Activity (или фрагмента) onResume() и onPause(). Это не элегантное решение, но оно будет работать.

Ответ 2

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

  public void surfaceCreated(SurfaceHolder holder) {     
          if (thread.getState==Thread.State.TERMINATED) { 
               thread = new MainThread(getHolder(),this);
          }
          thread.setRunning(true);
          thread.start();
  }

Ответ 3

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...
            }
        }
    }

Ответ 4

Лучший способ, который я нашел, - переопределить метод 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));
    }

Ответ 5

Пытался прокомментировать принятый ответ выше, но не смог, новичок в этом. Я не думаю, что вы должны называть свои методы запуска/остановки потоков как с SurfaceView, так и с Activity. Это приведет к запуску/остановке потока вдвойне, и вы не можете запускать поток более одного раза. Просто вызовите свои методы из Activity onPause и onResume. Они вызывается при выходе и повторном входе в приложение, чтобы это обеспечивало правильное управление вашими состояниями. surfaceDestroyed не всегда называется, что напутало меня на некоторое время.

Если вы используете этот метод, убедитесь, что перед тем, как работать с вашим холстом, проверьте действительную поверхность в своем коде запуска, потому что Activity запустит поток в onResume до того, как поверхность будет доступна.

        while (_run) {
            if (_surfaceHolder.getSurface().isValid()) {
                ...
            }
        } //end _run

Ответ 6

Это то, что я использовал. Приложение не вылетает сейчас.

Класс просмотра:

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){}
}

}

Надеюсь, это поможет.

Ответ 7

Вы должны использовать методы Activities onPause() и onResume().

Сначала, в surfaceCreated(), запустите поток. Кроме того, в onResume() убедитесь, что поток еще не запущен (сохраняйте переменную внутри потока или что-то еще). Затем, если он не запущен, установите его как запущенный снова. в onPause() приостановите поток. В surfaceDestroyed снова приостановите поток.

Ответ 8

Другое решение этой хорошо известной проблемы. К сожалению, я не понимаю, почему это работает - это получилось случайно. Но он работает хорошо для меня, и его легко реализовать: без переопределения Activity 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().

Извините за такой многопользовательский ответ. Я надеюсь, что он будет работать правильно и поможет.