Aplicando eventos del ciclo de vida en la actividad Juego de Asteroides

Ejercicio paso a paso: Aplicando eventos del ciclo de vida en la actividad Juego de Asteroides

Asteroides gestiona el movimiento de los objetos gráficos por medio de un thead que se ejecuta continuamente. Cuando la aplicación pasa a segundo plano, este thead continúa ejecutándose por lo que puede hacer que nuestro teléfono funcione más lentamente y además, gaste más batería.  Este problema aparece por una gestión incorrecta del ciclo de vida. El siguiente ejercicio veremos cómo solucionarlo:

1. Abre el proyecto Asteroides y ejecútalo en un terminal.

2.Pulsa el botón “Jugar” y cuando esté en mitad de la partida pulsa con el botón de Inicio (o Casa) para dejar a la actividad en estado parada. Las actividades en este estado no tendrían que consumir demasiados recursos. Sin embargo Asteroides sí que lo hace. Para verificarlos utiliza la aplicación “Administrador de tareas” (En las últimas versiones de Android, puedes abrirlo pulsando un segundo sobre el botón Casa y seleccionando el icono con forma de gráfico de tarta que aparece abajo a la izquierda).  El resultado puede ser similar al que se muestra a continuación.

NOTA: Si el administrador de tareas de tu terminal no te permite mostrar el porcentaje de uso de la CPU te recomendamos que instales un programa que te lo permita. Por ejemplo, OS Monitor.

Como puedes ver la aplicación Asteroides está consumiendo casi el 50% del uso de CPU. Evidentemente algo hemos hecho mal. No es lógico que cuando la actividad Juego esté en segundo plano se siga llamando a actualizaFisica().  En este ejercicio aprenderemos a solucionarlo.

3. Incluye la siguiente variable en la actividad Juego.

private VistaJuego vistaJuego;

4. Al final de onCreate añade:

vistaJuego = (VistaJuego) findViewById(R.id.VistaJuego);

5. Incorpora los siguientes métodos a la actividad:

@Override protected void onPause() {
   super.onPause();
   vistaJuego.getThread().pausar();
}
 
@Override protected void onResume() {
   super.onResume();
   vistaJuego.getThread().reanudar();
}
 
@Override protected void onDestroy() {
   vistaJuego.getThread().detener();
   super.onDestroy();
}

Lo que intentamos hacer con este código es poner en pausa el thread secundario cuando la actividad deje de estar activa y reanudarlo cuando la actividad recupere el foco. Además detener el thread cuando la actividad vaya a ser destruida.

NOTA: realmente el thread será destruido al destruirse la actividad que lo ha lanzado. No obstante puede resultar interesante hacerlo lo antes posible.

Observa como los métodos llamados por eventos del ciclo de vida solo pueden ser escritos un  Activity, por lo que no sería valido hacerlo en VistaJuego

6. Abre la clase VistaJuego y busca la definición de la clase ThreadJuego y reemplazala por la siguiente:

class ThreadJuego extends Thread {
   private boolean pausa,corriendo;
 
   public synchronized void pausar() {
          pausa = true;
   }
 
   public synchronized void reanudar() {
          pausa = false;
          notify();
   }
 
   public void detener() {
          corriendo = false;
          if (pausa) reanudar();
   }
  
   @Override public void run() {
          corriendo = true;
          while (corriendo) {
             actualizaFisica();
             synchronized (this) {
                while (pausa)
                   try {
                      wait();
                   } catch (Exception e) {
                   }
                }
             }
          }  
       }  
    }

Comenzamos declarando las variables pausa, corriendo. Estas pueden ser modificadas mediante los métodos pausar(), reanudar() y detener().

La palabra reservada synchronized impide el acceso de varios thread a una sección del código. En el capítulo anterior explicamos los hilos de ejecución.

El método run() se ha modificado de manera que en lugar de ser un bucle infinito, permitimos que termine poniendo la variable corriendo a false. Luego, tras llamar a actualiza Fisica(), se comprueba si se ha activado pausa. En tal caso, se entra en un bucle donde ponemos en espera el thread llamando al método wait(). Este quedará bloqueado hasta que se llame a notify(). Esta acción se realizará desde el método reanudar(). La llamada a wait() puede lanzar excepciones por lo que es obligatorio escribirla dentro de un bloque try {…} catch {…}.  

7. Dado que la variable thread es de tipo private, no puede ser manipulada desde fuera de VistaJuego. Para poder llamar a los métodos (pausar, reanudar,… ) de este objeto vamos a incluir un método getter. Para ello, sitúa el cursor justo antes de la última llave de VistaJuego. Pulsa con el botón derecho en el código y selecciona la opción Source / Generate Getters and Setters… Marca solo el método getThread() tal y como se muestra a continuación: 

Se insertará el siguiente código:

public ThreadJuego getThread() {
          return thread;

       }

8. Ejecuta de nuevo la aplicación y repite el segundo punto de este ejercicio. En este caso el resultado ha de ser similar al siguiente:

Práctica: Aplicando eventos del ciclo de vida en la actividad Juego para desactivar los sensores

En la unidad anterior aprendimos a utilizar los sensores para manejar la nave en Asteroides. El uso de sensores ha de realizarse con mucho cuidado dado su elevado consumo de batería. Resulta importante que cuando nuestra actividad quedara en un segundo plano se detuviera la lectura de los sensores, para que así no siga consumiendo batería.

1. Crea los métodos activarSensores() y desactivarSensores() en la clase VistaJuego.

2.  Mueve la línea de código que activa los sensores desde onCreate() al método activarSensores().

3.  Para desactivar los sensores hay que llamar al siguiente método:

mSensorManager.unregisterListener(SensorEventListener);

4. El parámetro de este método corresponde al objeto SensorEventListener del que queremos dejar de recibir eventos. En nuestro caso, nosotros mismos (this). Utiliza este método dentro de desactivarSensores().

5. Utiliza los métodos del ciclo de vida adecuados para activar o desactivar los sensores. Recuerda que estos métodos los recoge la activad, por lo que tendrás que incluirlos en la clase Juego.