2010-08-20 4 views
14

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

ответ

11

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

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

+0

Мне нравится идея ура, я работаю, чтобы найти что-то легкое. Потому что «surfaceDestroyed» не вызывается каждый раз, но «onPause» есть. Как и нажатие кнопки «power», вернитесь. Поэтому я думаю, что ваш выбор действительно хороший. –

0

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

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

+0

Я его правильно настроить, чтобы использовать setRunning в соответствующих местах, но, несмотря на thread.setRunning (правда) в моем onResume мой SurfaceView пустым, когда его обратно на переднем крае. Поток все еще существует, он не разбивает приложение, когда я перехожу на главный экран, и если я попробую другой thread.start(), я получаю ошибку, говоря, что потоки были запущены. Есть идеи? – jfisk

+0

Вы можете запускать только один раз. Я не знаю, что делает ваш метод setRunning внутри, но единственный способ правильно остановить поток на Android - позволить ему вернуться из метода run(). С этого момента вы должны «создать новый объект потока». Вы не можете использовать этот поток еще раз. Если вы хотите «приостановить» поток, вы должны использовать wait() и notifyAll(). Google, например, и правильное понимание. – Moncader

2
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... 
      } 
     } 
    } 
+0

Я изучаю поверхностное видение, но не могу угадать, почему вы не используете перерыв stamenet во время цикла. Моя программа замерла во время onResume(), но после того, как я положил перерыв, она начала ажиотировать, ясно, что был создан новый поток. Это мой код и вопрос: http://stackoverflow.com/questions/19200972/android-surfaceview-cant-resume-activity-from-pause – Luther

+0

Читайте о Thread.join(), тогда вы поймете. –

+0

Деятельность 'onPause' не будет ** всегда ** вызывать' surfaceDestroyed' самостоятельно. Так что это само по себе не решит проблему. См. Этот вопрос http://stackoverflow.com/q/11495842/1180117 – Kiran

0

Другим решение для этого хорошей известной проблемы. К сожалению, я не понимаю, почему это работает - это получилось случайно. Но это работает хорошо для меня, и его легко реализовать: не требуется переопределение ActivityonPause(), onResume(), onStart(), onStop(), а также не требуется написание специальных методов потоков (например, resume(),).

Особое требование - поместить все изменяющиеся переменные в нечто иное, чем рендеринг класса потока.

Основные моменты добавить в рендер-нить класс:

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().

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

+0

Деятельность 'onPause' не будет ** всегда ** вызывать' surfaceDestroyed' самостоятельно. Так что это не решит проблему. См. Этот вопрос http://stackoverflow.com/q/11495842/1180117 – Kiran

1

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

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

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

a retracted comment – tjb

2

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

Это сделало трюк для меня. Когда я попробовал это только в onResume, это не сработало. – sivi

1

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

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

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

} 

Я надеюсь, что это поможет.

+0

Деятельность 'onPause' не будет ** всегда ** вызывать' surfaceDestroyed' самостоятельно. Так что это не решит проблему. См. Этот вопрос http://stackoverflow.com/q/11495842/1180117 – Kiran

4

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

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

Деятельность 'onPause' не будет ** всегда ** вызывать' surfaceDestroyed' самостоятельно. Так что это само по себе не решит проблему. См. Этот вопрос http://stackoverflow.com/q/11495842/1180117 – Kiran

Смежные вопросы