как приостановить и возобновить поток 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().

извините за такой пространственный ответ. Надеюсь, он будет работать правильно и поможет.