2010-08-20 28 views
14

Tengo una configuración de SurfaceView y en ejecución, pero cuando lo reanudo me sale un error de que el subproceso ya se ha iniciado. ¿Cuál es la forma correcta de manejar cuando la aplicación pasa al segundo plano y luego vuelve al primer plano? He estado haciendo pequeños cambios y logré que la aplicación vuelva sin fallar ... pero SurfaceView ya no dibuja anthing. Mi código:cómo pausar y reanudar un subproceso de vista de superficie

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

Respuesta

11

La solución fácil es simplemente matar y reiniciar el hilo. Cree métodos resume() - crea el objeto thread y lo inicia - y pause() - kill thread (ver ejemplo Lunarlander) - en su clase SurfaceView y los llama desde surfaceCreated y surfaceDestroyed para iniciar y detener el hilo.

Ahora, en la Actividad que ejecuta SurfaceView, también deberá llamar a los métodos resume() y pause() en SurfaceView desde Activity (o los fragmentos) onResume() y onPause(). No es una solución elegante, pero funcionará.

+0

Me encanta la idea de Ur, he estado trabajando para encontrar algo fácil. Porque "surfaceDestroyed" no se llama cada vez, pero "onPause" sí lo es. Al igual que presionar el botón de "encendido", regrese. Así que creo que tu elección es realmente buena. –

0

Debe utilizar los métodos Activities onPause() y onResume().

Primero, en surfaceCreated(), inicie el hilo. Además, en onResume(), asegúrese de que el hilo no se haya iniciado todavía (mantenga una variable dentro del hilo o algo así). Luego, si no está funcionando, configúrelo como ejecutando nuevamente. en onPause(), pausa el hilo. En surfaceDestroyed, pausa el hilo nuevamente.

+0

tengo correctamente configurado para utilizar setRunning en los lugares apropiados, pero a pesar de tener thread.setRunning (verdadero) en mi onResume mi SurfaceView está en blanco cuando su espalda a la vanguardia El hilo todavía está allí, no bloquea la aplicación cuando voy a la pantalla de inicio, y si pruebo con otro thread.start() aparece el error que indica que los hilos se han iniciado. ¿Algunas ideas? – jfisk

+0

Solo puede iniciar un hilo una vez. No sé qué hace el método setRunning en el interior, pero la única forma de detener correctamente un hilo en Android es dejar que regrese de su método run(). A partir de ese momento, debe 'crear' un nuevo objeto de hilo. No puedes usar ese hilo nunca más. Si desea 'pausar' el hilo, debe usar wait() y notifyAll(). Google alrededor, por ejemplo, y una comprensión adecuada de eso. – 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

Estoy aprendiendo la vista de superficie, pero no puedo entender por qué no está utilizando un stamenet de salto en el ciclo while. Mi programa se congeló durante onResume(), pero después de poner break comenzó a funcionar, claramente se creó un nuevo hilo. Este es mi código y pregunta: http://stackoverflow.com/questions/19200972/android-surfaceview-cant-resume-activity-from-pause – Luther

+0

Lea acerca de Thread.join(), entonces comprenderá. –

+0

La actividad 'onPause' no ** siempre ** invocar' surfaceDestroyed' por sí mismo. Entonces esto solo no resolverá el problema. Consulte esta pregunta http://stackoverflow.com/q/11495842/1180117 – Kiran

0

Otra solución para este problema bien conocido. Lamentablemente, no entiendo por qué funciona, salió accidentalmente. Pero funciona bien para mí y es fácil de implementar: no se requiere la anulación de ActivityonPause(), onResume(), onStart(), onStop(), ni la escritura de métodos de subprocesos especiales (como resume(), pause()).

El requisito especial es poner todas las variables cambiantes en algo que no sea la clase de subproceso de procesamiento.

puntos principales de agregar para hacer hilos de clase:

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

Ahora, las cosas importantes acerca 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) { 
     }    
    } 
} 

Dos bloques de código anterior no son clases de pleno escrito, pero la mera noción de qué comandos en qué métodos se necesitan. También tenga en cuenta que cada devolución a la aplicación invoca surfaceChanged().

Lo siento por esta respuesta que consume mucho espacio. Espero que funcione correctamente y ayudará.

+0

La actividad 'onPause' no ** siempre ** invocar' surfaceDestroyed' por sí mismo. Entonces esto no resolverá el problema. Consulte esta pregunta http://stackoverflow.com/q/11495842/1180117 – Kiran

1

Trató de comentar la respuesta aceptada anteriormente pero no pudo, como novedad en esto. No creo que deba llamar a sus métodos de inicio/detención de subprocesos desde su SurfaceView y su actividad. Esto dará como resultado el inicio/la detención doble del hilo y no podrá iniciar un hilo más de una vez. Simplemente llame a sus métodos desde Activity onPause y onResume. Se llaman al salir y al volver a ingresar a la aplicación para asegurarse de que sus estados se manejen correctamente. surfaceDestroyed no siempre se llama, lo que me fastidió por un tiempo.

Si utiliza este método, asegúrese de comprobar una superficie válida en su código de ejecución antes de trabajar con su lienzo, ya que la actividad iniciará el subproceso en Repetición antes de que la superficie esté disponible.

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

un comentario retractado – tjb

2

La mejor manera que he encontrado es reemplazar el método onResume de la actividad de control de la vista de la superficie de manera que con el método de re-crea una instancia del SurfaceView y a continuación, establece con setContentView. El problema con este enfoque es que debe volver a cargar cualquier estado del que se esté ocupando su SurfaceView.

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

     setContentView(new MyCustomSurfaceView(this)); 
    } 

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

Eso me sirvió de algo. Cuando lo probé antes solo en el onResume, no funcionó. – sivi

1

Esto es lo que he usado. La aplicación no se bloquea ahora.

Vista de clases:

holder.addCallback(new Callback() { 

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

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

     } 

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

} 

espero que le ayudará.

+0

La actividad 'onPause' no ** siempre ** invocar' surfaceDestroyed' por sí mismo. Entonces esto no resolverá el problema. Consulte esta pregunta http://stackoverflow.com/q/11495842/1180117 – Kiran

4

Este error parece estar relacionado con el error de aterrizaje lunar, que es bastante famoso (hacer una búsqueda en Google en él). Después de todo este tiempo, y después de varias versiones de la versión de Android, el error aún existe y nadie se ha molestado en actualizarlo. he encontrado que esto funcione con el código menos el desorden:

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

La actividad 'onPause' no ** siempre ** invocar' surfaceDestroyed' por sí mismo. Entonces esto solo no resolverá el problema. Ver esta pregunta http://stackoverflow.com/q/11495842/1180117 – Kiran

Cuestiones relacionadas