2010-08-20 6 views
14

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를 실행하는 활동에서 ActivityView의 resume() 및 pause() 메소드를 Activity (또는 조각)의 onResume() 및 onPause()에서 호출해야합니다. 그것은 우아한 해결책은 아니지만 작동 할 것입니다.

+0

나는 우르 생각을 좋아해, 나는 쉬운 일을 찾기 위해 일하고있다. "surfaceDestroyed"는 매번 호출되지 않기 때문에 "onPause"가 호출되기 때문입니다. 그냥 "전원"버튼을 누른 다음 돌아갑니다. 그래서 당신의 선택이 정말로 좋은 것이라고 생각합니다. –

0

Activities onPause() 및 onResume() 메소드를 사용해야합니다.

먼저, surfaceCreated()에서 스레드를 시작하십시오. 또한 onResume()에서 스레드가 아직 시작되지 않았는지 확인하십시오 (스레드 또는 다른 변수 내부의 변수 유지). 그런 다음 실행 중이 아니면 다시 실행으로 설정하십시오. onPause()에서 스레드를 일시 중지합니다. surfaceDestroyed에서 스레드를 다시 일시 중지하십시오.

+0

내가 제대로 적절한 장소에서 setRunning를 사용하도록 설정해야하지만 thread.setRunning (사실)에도 불구하고 내 onResume 내 서피스 뷰 SurfaceView 빈 경우는 다시 최전선에있다. 스레드는 여전히 거기에, 그것은 집 화면에 갈 때 응용 프로그램을 크래시, 그리고 만약 내가 다른 thread.start() 시도 스레드를 시작했다는 오류가 나타납니다. 어떤 아이디어? – jfisk

+0

한 번만 스레드를 시작할 수 있습니다. 귀하의 setRunning 메서드가 내부에서 무엇을하는지 모르겠지만, 안드로이드에서 스레드를 올바르게 정지시키는 유일한 방법은 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

나는 surfaceview를 배우고 있지만 while 루프에서 break stamenet을 사용하지 않는 이유를 알 수 없습니다. 내 프로그램은 onResume() 중에 동결되었지만 휴식을 넣은 후에는 agin이 시작되어 분명히 새 스레드가 만들어졌습니다. 이것은 내 코드 및 질문입니다. http://stackoverflow.com/questions/19200972/android-surfaceview-cant-resume-activity-from-pause – Luther

+0

Thread.join()에 대해 읽으면 이해할 수 있습니다. –

+0

Activity'onPause'는 항상 ** 자신이 가지고있는'surfaceDestroyed'를 ** 호출하지 않습니다. 그래서 이것만으로도 문제가 해결되지는 않습니다. 이 질문을 참조하십시오 http://stackoverflow.com/q/11495842/1180117 – Kiran

0

이 좋은 알려진 문제에 대한 또 다른 해결책. 슬프게도, 나는 그것이 왜 작동하는지 이해하지 못한다. 하지만 좋은 일을하고 구현하기가 쉽습니다. onPause(), onResume(), onStart(), onStop()을 무시하지 말고 특별한 스레드 메소드 (예 : resume(), pause())를 쓰지 않아도됩니다.

특별한 요구 사항은 모든 변경 변수를 스레드 클래스 렌더링 이외의 다른 것으로 바꾸는 것입니다.

홈페이지 포인트를 추가하려면 렌더 스레드 클래스 :

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가 항상 호출되는 것은 아니며 잠시 나를 망 쳤습니다.

이 방법을 사용하는 경우, 캔버스를 사용하기 전에 실행 코드에서 유효한 표면을 확인하십시오. 왜냐하면 작업은 표면이 사용 가능해지기 전에 onResume에서 스레드를 시작하기 때문입니다.

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

철회 된 의견 – tjb

2

I 찾은 최선의 방법은 그것이 상기 방법으로 서피스 뷰 SurfaceView 다시 인스턴스화되도록 표면보기 제어 활동 onResume 방법을 대체 한 후 된 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

이것은 내가 사용했던 것입니다. 앱이 지금 다운되지 않습니다.

보기 클래스 다음 GameLoopThread에서

holder.addCallback(new Callback() { 

     public void surfaceDestroyed(SurfaceHolder holder) { 
      gameLoopThread.setRunning(false); 
      gameLoopThread.stop(); 
     } 

     public void surfaceCreated(SurfaceHolder holder) { 
      gameLoopThread.setRunning(true); 
      gameLoopThread.start(); 

     } 

:

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 검색으로 유명합니다. 이 시간이 지나고 여러 안드로이드 버전이 출시 된 후에도 버그는 여전히 존재하며 아무도 업데이트하지 않으려 고합니다. 나는 이것이 최소한의 코드 혼란 작업을 발견 :

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

관련 문제