2010-08-20 5 views
14

Ich habe ein Setup von SurfaceView und laufe, aber wenn ich es weiterführe, erhalte ich einen Fehler, dass der Thread bereits gestartet wurde. Was ist der richtige Weg, wenn die App in den Hintergrund und dann wieder in den Vordergrund geht? Ich habe herumgebastelt und es geschafft, dass die App ohne Absturz zurückkam ... aber der SurfaceView zieht nicht mehr an. Mein Code:Wie pausiere ich und setze einen SurfaceView-Thread fort

Antwort

11

Die einfache Lösung ist einfach den Thread zu töten und neu zu starten. Methoden erstellen resume() - erstellt ein Thread-Objekt und startet es - und pause() - killt Thread (siehe Lunarlander-Beispiel) - in Ihrer SurfaceView-Klasse und ruft diese von SurfaceCreated und SurfaceDestroyed auf, um den Thread zu starten und zu stoppen.

Jetzt in der Aktivität, die die SurfaceView ausführt, müssen Sie auch die resume() - und pause() -Methoden in der SurfaceView aus den Aktivitäten onResume() und onPause() der Aktivität (oder des Fragments) aufrufen. Es ist keine elegante Lösung, aber es wird funktionieren.

+0

Ich liebe Ur Idee, ich habe gearbeitet, um etwas leicht zu finden. Denn "olddestroyed" wird nicht immer aufgerufen, sondern "onPause" ist. Wie beim Drücken der Taste "Power", kehren Sie zurück. Also denke ich, dass deine Wahl wirklich gut ist. –

0

Sie sollten die Aktivitäten onPause() und onResume() -Methoden verwenden.

Starten Sie zuerst in threadedCreated() den Thread. Stellen Sie in onResume() außerdem sicher, dass der Thread nicht bereits gestartet wurde (behalten Sie eine Variable innerhalb des Threads oder etwas). Wenn es nicht ausgeführt wird, legen Sie es erneut als ausgeführt fest. Halte in onPause() den Thread an. In paneurDestroyed pausiere den Thread erneut.

+0

Ich habe es richtig zu nutzen setRunning an den entsprechenden Stellen gesetzt, aber trotz thread.setRunning (true) in meinem onResume meines Surface mit leer ist, wenn sein Rücken im Vordergrund. Der Thread ist immer noch da, es stürzt die App nicht ab, wenn ich auf den Home-Bildschirm gehe, und wenn ich einen anderen thread.start() versuche, bekomme ich den Fehler, dass die Threads gestartet wurden. Irgendwelche Ideen? – jfisk

+0

Sie können einen Thread nur einmal starten. Ich weiß nicht, was Ihre setRunning-Methode auf der Innenseite tut, aber die einzige Möglichkeit, einen Thread unter Android ordnungsgemäß zu stoppen, ist, ihn von seiner run() -Methode zurückkehren zu lassen. Von diesem Zeitpunkt an müssen Sie ein neues Thread-Objekt erstellen. Sie können diesen Thread nicht mehr verwenden. Wenn Sie den Thread "pausieren" wollen, müssen Sie wait() und notifyAll() verwenden. Google zum Beispiel und das richtige Verständnis dafür. – 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

Ich lerne Oberflächenansicht, kann aber nicht verstehen, warum Sie kein Break-Stamenet in der While-Schleife verwenden. Mein Programm ist bei onResume() erstarrt, aber nachdem ich Pause gemacht habe, fing es an zu aginieren, eindeutig wurde ein neuer Thread erstellt. Das ist mein Code und meine Frage: http://stackoverflow.com/questions/19200972/android-surfaceview-cant-resume-activity-from-pause – Luther

+0

Lesen Sie über Thread.join() dann werden Sie verstehen. –

+0

Die Aktivität 'onPause' ruft nicht ** immer **' surfaceDestroyed' auf. Das allein wird das Problem nicht lösen. Siehe diese Frage http://StackOverflow.com/q/11495842/1180117 – Kiran

0

Eine andere Lösung für dieses gut bekanntes Problem. Leider verstehe ich nicht, warum es funktioniert - es kam zufällig heraus. Aber es funktioniert gut für mich und es ist einfach zu implementieren: kein Überschreiben von 's onPause(), onResume(), onStart(), onStop(), noch Schreiben von speziellen Thread-Methoden (wie resume(), pause()) erforderlich sind.

Eine spezielle Anforderung besteht darin, alle sich ändernden Variablen in eine andere als die Rendering-Thread-Klasse einzufügen.

Hauptpunkte auf Add-Thread zu machen Klasse:

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

Nun wichtige Dinge über 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) { 
     }    
    } 
} 

Zwei Codeblöcke oben sind nicht voll geschriebenen Klassen, sondern bloße Vorstellung welche Befehle in welchen Methoden benötigt werden. Beachten Sie außerdem, dass jede Rückkehr zur App surfaceChanged() aufruft.

Sorry für solche platzraubende Antwort. Ich hoffe, es wird richtig funktionieren und wird helfen.

+0

Aktivität 'onPause' ruft nicht ** immer **' SurfaceDestroyed' auf. Das wird das Problem also nicht lösen. Siehe diese Frage http://StackOverflow.com/q/11495842/1180117 – Kiran

1

Versucht, die angenommene Antwort oben zu kommentieren, konnte aber nicht, neu dazu. Ich denke nicht, dass Sie Ihre Start/Stop-Thread-Methoden sowohl von SurfaceView als auch von Activity aufrufen sollten. Dies führt dazu, dass der Thread doppelt gestartet/gestoppt wird und Sie einen Thread nicht mehr als einmal starten können. Rufen Sie einfach Ihre Methoden aus den Aktivitäten onPause und onResume auf. Sie werden aufgerufen, wenn Sie die App verlassen und erneut eingeben, um sicherzustellen, dass Ihre Status korrekt gehandhabt werden. SurfaceDestroyed heißt nicht immer, was mich für eine Weile durcheinander gebracht hat.

Wenn Sie diese Methode verwenden, überprüfen Sie vor dem Arbeiten mit der Zeichenfläche, ob eine gültige Oberfläche in Ihrem Ausführungscode vorhanden ist, da die Aktivität den Thread in onResume startet, bevor die Oberfläche verfügbar ist.

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

ein zurückgezogener Kommentar – tjb

2

Der beste Weg, die ich gefunden habe ist die onResume Methode der Aktivität Steuerung der Oberflächenansicht außer Kraft zu setzen, so dass mit dem Verfahren des Surface erneut instanziiert und dann setzt es mit setContentView. Das Problem bei diesem Ansatz besteht darin, dass Sie jeden Zustand neu laden müssen, für den sich Ihr SurfaceView gekümmert hat.

public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(new MyCustomSurfaceView(this)); 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     setContentView(new MyCustomSurfaceView(this)); 
    } 
+0

Das hat den Trick für mich getan. Als ich es vorher nur im onResume ausprobiert habe es nicht geklappt. – sivi

1

Dies ist, was ich verwendet habe. Die App stürzt jetzt nicht ab.

Ansicht Klasse:

holder.addCallback(new Callback() { 

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

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

     } 

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

} 

Ich hoffe, es hilft.

+0

Die Aktivität 'onPause' ruft nicht ** immer **' toiletDestroyed' auf. Das wird das Problem also nicht lösen. Siehe diese Frage http://StackOverflow.com/q/11495842/1180117 – Kiran

4

Dieser Fehler scheint sich auf den Mondlandefährer zu beziehen, der ziemlich berühmt ist (eine Google-Suche darauf machen). Nach all dieser Zeit, und nach mehreren Android-Versionen veröffentlicht, ist der Fehler immer noch vorhanden und niemand hat sich die Mühe gemacht, es zu aktualisieren. Ich habe dies mit der geringstenen Code Unordnung arbeiten gefunden:

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

Aktivität 'onPause' wird nicht ** immer **' SurfaceDestroyed' von sich aus aufrufen. Das allein wird das Problem nicht lösen. Siehe diese Frage http://stackoverflow.com/q/11495842/1180117 – Kiran

Verwandte Themen