Bienvenidos sean a este post, hoy continuaremos con lo iniciado en el post anterior es decir los ciclos de vida de una actividad en el post de hoy veremos como considera el sistema la eliminacion de un proceso porque como dijimos previamente los procesos de las actividades pueden ser eliminadas por nosotros o en ciertas circunstancias por el S.O, por ejemplo cuando la memoria esta llena y debemos ejecutar una nueva app.

Anuncios

Para lograr esto, el S.O asigna una jerarquia o una prioridad a los procesos, esta  se confecciona en base a los componentes que se estan ejecutando (actividades y servicios) y el estado de los mismos, a continuacion veremos un listado de las jerarquias de los procesos:

  • Proceso de primer plano (Foreground process), se considera a la actividad que esta interactuando con el usuario, deberia haber uno solo de este estilo o muy pocos, y como suponemos sera el ultimo tipo de proceso en ser eliminado
  • Proceso visible (Visible process), es cuando la actividad esta en pantalla pero no en primer plano, entra en estado onPause(), no sera eliminada salvo que sea necesario para una actividad en primer plano
  • Proceso de servicio (Service process), es cuando se inicia por medio de startService(), estos no son visibles para el usuario pero se encargan de ejecutar actividades como puede ser reproducir un MP3 o mantener una conexion con un servidor, estos no van a ser eliminados por el sistema salvo que los tipos de procesos anteriores corran riesgo por poca memoria
  • Proceso de fondo (Background process), es cuando una actividad deja de ser vista por un usuario, se llamo al evento onStop(), el S.O observara cual fue la ultima actividad vista por el usuario y sera la ultima en ser eliminada en caso de ser necesario, por lo general no afectan a la experiencia del usuario
  • Proceso vacio (Empty process), aca no se hospeda ningun componente pero se utiliza como un cache para mejorar el tiempo de iniciacion para la proxima actividad que ejecutemos

Para entender un poco mejor el concepto de los procesos vamos a hacer una pequeña modificacion en nuestra app Asteroides, para ello primero vamos a descargar el siguiente archivo:

Una vez descargado abramos nuestro proyecto con Android Studio pero antes vayan a la carpeta donde descargaron el archivo copienlo por medio de Ctrl+C o con el boton derecho y la opcion Copiar, vayan dentro del Android Studio y luego vayan hasta el recurso res/raw y por medio de Ctrl+V o boton derecho y la opcion Pegar para agregar nuestro nuevo recuros una vez hecho nos quedara de la siguiente forma

Con nuestro nuevo recurso procedamos a modificar el codigo de MainActivity agregando las siguientes lineas en el metodo onCreate():

MediaPlayer mp = MediaPlayer.create(this, R.raw.audio);
mp.start();
Nota: si no les agrega el archivo o les dice que no lo encuentra, ejecuten File -> sync project with Gradle files para sincronizar el nuevo recurso

Ahora probemos la app, una vez abierta deberian estar escuchando el archivo de audio agregado, presionen el boton para volver al home y deberia seguir sonando la musica para luego detenerse como se ve en el siguiente video

Anuncios

Para poder corregir esto deberemos hacer las siguientes modificaciones:

Primero declararemos nuestro objeto de la clase MediaPlayer al principio de la clase:

private MediaPlayer mp;

Luego modificaremos la linea para cargar la musica en el metodo onCreate() de la siguiente forma:

mp = MediaPlayer.create(this, R.raw.audio);

Luego modificaremos el metodo onStop() agregando la siguiente linea:

mp.pause();

Esta se encargara de detener la musica y por ultimo modificaremos el metodo onResume() para agregar la siguiente linea:

mp.start();

Con esto reanudaremos la musica en nuestro juego, por ende el nuevo codigo de MainActivity debera quedar de la siguiente forma:

package org.example.asteroides;

import android.content.Intent;
import android.content.SharedPreferences;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.Prediction;
import android.media.MediaPlayer;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity
        implements GestureOverlayView.OnGesturePerformedListener{

    private GestureLibrary librearia;
    private MediaPlayer mp;

    public static AlmacenPuntuaciones almacen =
            new AlmacenPuntuacionesArray();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LinearLayout jugar = (LinearLayout) findViewById(R.id.principio);
        Animation animacion = AnimationUtils.loadAnimation(this,
                R.anim.aparecer);
        jugar.startAnimation(animacion);
        librearia = GestureLibraries.fromRawResource(this,R.raw.gestures);
        if (!librearia.load())
            finish();

        GestureOverlayView gestosView = (GestureOverlayView)
                findViewById(R.id.gestos);
        gestosView.addOnGesturePerformedListener(this);
        Toast.makeText(this,"onCreate",Toast.LENGTH_SHORT).show();
        mp = MediaPlayer.create(this, R.raw.audio);
        mp.start();
    }
    public void lanzarJuego(View view){
        Intent i = new Intent(this, Juego.class);
        startActivity(i);
    }
    public void lanzarAcercaDe(View view){
        Intent i = new Intent(this, AcercaDe.class);
        startActivity(i);
    }
    public void lanzarPreferencias(View view){
        Intent i = new Intent(this, Preferencias.class);
        startActivity(i);
    }

    public void lanzarPuntuaciones(View view){
        Intent i = new Intent(this, Puntuaciones.class);
        startActivity(i);
    }

    public void mostrarPreferencias(View view){
        SharedPreferences pref =
                PreferenceManager.getDefaultSharedPreferences(this);
        String s = "música: " + pref.getBoolean("musica",true)
                + ", graficos: " + pref.getString("graficos","?");
        Toast.makeText(this,s,Toast.LENGTH_SHORT).show();
    }

    public void onGesturePerformed(GestureOverlayView ov, Gesture gesto){
        ArrayList<Prediction> predicciones = librearia.recognize(gesto);
        if (predicciones.size()>0){
            String comando = predicciones.get(0).name;
            if (comando.equals("play")){
                lanzarJuego(null);
            } else if(comando.equals("configurar")){
                lanzarPreferencias(null);
            } else if(comando.equals("acerca_de")){
                lanzarAcercaDe(null);
            } else if(comando.equals("cancelar")){
                finish();
            }
        }
    }

    @Override
    protected void onStart(){
        super.onStart();
        Toast.makeText(this,"onStart",Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onResume(){
        super.onResume();
        Toast.makeText(this,"onResume",Toast.LENGTH_SHORT).show();
        mp.start();
    }

    @Override
    protected void onPause(){
        super.onPause();
        Toast.makeText(this,"onPause",Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onStop(){
        super.onStop();
        Toast.makeText(this,"onStop",Toast.LENGTH_SHORT).show();
        mp.pause();
    }

    @Override
    protected void onRestart(){
        super.onRestart();
        Toast.makeText(this,"onRestart",Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        Toast.makeText(this,"onDestroy",Toast.LENGTH_SHORT).show();
    }
}

Anuncios

Con todas estas modificaciones realizadas podemos probar nuestra app y ahora deberia funcionar correctamente como se ve en el siguiente video

Como se ve en el video la musica solo se interrumpe cuando la actividad quedara fuera de la vista del usuario mientras este en pantalla, como por ejemplo con el Acerca de, seguira sonando.

Para nuestro siguiente caso veremos como guardar el estado de una actividad para en los casos de ser eliminadas por el S.O esta vuelva a su ultimo estado para que el usuario no sienta ninguna diferencia, para ello nosotros vamos a utilizar dos tipos de eventos, el onPause() para grabar el estado y con onResume() para recuperarlo, esto es siempre hablando de los estados de la actividad para informacion mas sensible se deben utilizar metodos de almacenamiento mas seguro y estables, tambien tenemos dos metodos adicionales para poder trabajar con esto, veamos cuales son:

  • onSaveInstanceState(Bundle), se permite a la actividad invocarla para grabar su estado pero si hay poca memoria la actividad puede ser destruida sin ser invocado
  • onRestoreInstanceState(Bundle), se utiliza para recuperar el estado guardado por el metodo anterior

Vamos a ver un ejemplo para ver como funciona, para ello volvamos a nuestra app Asteroides, vamos a nuestra clase MainActivity y agregaremos los siguiente metodos:

@Override
protected void onSaveInstanceState(Bundle estadoGuardado){
    super.onSaveInstanceState(estadoGuardado);
    if (mp!=null){
        int pos = mp.getCurrentPosition();
        estadoGuardado.putInt("posicion",pos);
    }
}

@Override
protected void onRestoreInstanceState(Bundle estadoGuardado){
    super.onRestoreInstanceState(estadoGuardado);
    if (estadoGuardado!=null && mp!=null){
        int pos = estadoGuardado.getInt("posicion");
        mp.seekTo(pos);
    }
}

El primero metodo se encargara de grabar nuestro estado, para ello almacenara en una variable llamado pos la posicion del audio que se reproduce en el objeto mp, esto lo hace por medio del metodo putInt() siempre y cuando mp sea distinto de null, esto por medio del condicional if, nuestro siguiente metodo se encargara de recuperar la informacion almacenada en estadoGuardado y lo buscara en el objeto mp.
Esto nos permitira girar nuestro dispositivo sin interrumpir la reproduccion de la musica de fondo, como siempre con el unico objetivo de hacer una experiencia mas confortable al usuario.

Anuncios

En resumen, hoy hemos seguido con los ciclos de vida de una app, en este caso hemos visto que puede ocasionar la finalizacion de una actividad, como se jerarquizan y sus nombres, bajo cual concepto pueden ser finalizados y por ultimo como guardar y recuperar el estado actual de una actividad en este caso con la musica de nuestra app Asteroides, espero les haya sido util sigueme en Twitter, Facebook o Google+ para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00