Android tiene una forma peculiar de gestionar las aplicaciones que choca un poco con los sistemas operativos tradicionales. Pese a estar montado en un linux con el kernel 2.6, Android no tiene por costumbre matar las aplicaciones cuando las cerramos. La clave está en que igual lo hace o igual no, según las necesidades o lo que Android considere oportuno. La cosa es que no hay que empeñarse en tener un botón para matar la aplicación, oficialmente no se recomienda y se todo se resume a un tranqui, Android se preocupa.

Como usuario habitual de linux y gran devoto de Nuestra Señora del kill -9 esto de no poder MATAR procesos (o siendo precisos mandar la señal de la muerte SIGKILL) es algo que no me gusta pero... lo han diseñado así y parece que el nuevo windows va por el mismo camino. Como decían unos MVP a los que vi tiempo atrás: "Windows Vista no chupa recursos, lo que hace es... aprovecharlos" con un par. Así que la idea es barra libre, todos los programas abiertos y si nos quedamos sin nada ya iremos matando. La idea no es mala del todo ya que en teoría mantienes todo abierto y no hay que esperar a que los programas se abran y eso es coherente con la forma de usar el móvil en la que saltas de una pantalla a otra y puedes ser interrumpido por llamadas.

Los estados
LifeCycle de android

Estos son los estados por los que puede pasar una aplicación en Android:

  • onCreate: es el primer método en ser ejecutado por la Activity una única vez hasta que se destruye.
  • onStart: se ejecuta inmediatamente después de onCreate u onRestart, y es previo a la visualización de la pantalla.
  • onResume: se ejecuta inmediatemente después de onStart u onPause, la actividad se muestra al usuario y esta pasa a primer plano.
  • onPause:se invoca previamente al Stop cuando la actividad sale del primer plano. Aquí es donde se suele aprovechar para salvaguardar datos. Podemos distinguir si la aplicación va a pausarse o a terminarse.
  • onStop: tras la pausa puede venir el stop. Aquí se liberan recursos, OJO si andamos por ejemplo con SQLite ya que el Stop se carga los cursores.
  • onRestart: volvemos al start y se puede ejecutar desde el estado de Stop.
  • onDestroy: simétrico del create, cuando la aplicación se destruye definitivamente por Android.
El trauma del cambio de orientación de pantalla

Al cambiar de orientación la pantalla, algo que al usuario le parece una pijada, para la App estás provocando un verdadero terremoto. La aplicación salva su estado (si sobrescribes ese método) se pausa (pause), se para (stop), se destruye (destroy), se crea (create) otra vez, se inicia (start) y se recuperan los valores salvados anteriormente. Si estás con el emulador tienes puedes hacer Ctrl-F12 para hacer cambios de orientación. Ojo, el dispositivo debe soportarle y si no lo tiene lo debes agregar en sus opciones de hardware.

Probando el ciclo de vida

Esta es un App para obvservar el ciclo de vida. En su día ya la hice pero esta es algo más decente, es la clásica Activity y sobreescribe todos los handlers vinculados al ciclo de vida de una App de android. Y de paso también prueba el tema de guardar contenidos un Bundle (lo que viene a ser un saco para guardar el estado de la aplicación), que luego se pueden recuperar en el Create. La aplicación hay que probarla con el DDMS ya que lo único que hace es mandar mensajes al logcat. Probándolo en un 2.2 y un 4.0 me ha chocado que el botón Back para la App y finalmente Android destruye la App. En cambio con el Home siempre se pausa y sí que activa el método para guardar el estado. En un Activity se supone que hacer un this.finish es equivalente al botón Back, pero como decía no hay un botón de matar. A través de la configuración de Android, dentro de aplicaciones sí que tenemos un botón para matar y cargarnos la memoria que esté utilizando cualquier App.

package info.pello.android.lifecycle;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;

/**
 * Simple Activity to observe Android App lifecycle.
 * @author Pello Xabier Altadill Izura
 * @greetz 4 u as always
 */
public class MainActivity extends Activity {

	private static int counter = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		log("App created");
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		if (null != savedInstanceState) {
			Log.d("Restored value1: " + savedInstanceState.getString("Something") , "PELLODEBUG");
			Log.d("Restored value2: " + savedInstanceState.getString("Another") , "PELLODEBUG");
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		log("Options menu created");
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	/**
	 * called from the button and tells Android that
	 * this should be closed.
	 * @param v
	 */
	public void stopApp (View v) {
		this.finish();
	}

	/**
	 * Suposed to be called when restoring app when:
	 * 1. Screen orientation changes
	 * 2. App was killed by android
	 * But:
	 *     "Most implementations will simply use onCreate(Bundle) to restore their state, but it is sometimes convenient to do it here after all of the initialization has been done or to allow subclasses to decide whether to use your default implementation. The default implementation of this method performs a restore of any view state that had previously been frozen by onSaveInstanceState(Bundle)."
	 * There is no reason to override onRestoreInstanceState() unless you are subclassing Activity and it is expected that someone will subclass your subclass.
	 * @param bundle data to restore app state
	 */
	@Override
	protected void onRestoreInstanceState(Bundle bundle) {
		super.onRestoreInstanceState(bundle);
		log("Restoring Instance State");
		Log.d("Restored value1: " + bundle.getString("Something") , "PELLODEBUG");
		Log.d("Restored value2: " + bundle.getString("Another") , "PELLODEBUG");
	}

	/**
	 * when user press home button this is
	 * called before app is paused
	 */
	@Override
	protected void onSaveInstanceState(Bundle bundle) {
		super.onSaveInstanceState(bundle);
		// Save some values AFTER!! invoking super method
		bundle.putString("Something", "Saved for later");
		bundle.putString("Another", "Another Saved for later");
		log("Saving instance State...");
	}

	/**
	 * called after the app is Created
	 * or after stop > restart
	 */
	@Override
	protected void onStart () {
		super.onStart();
		log("App Started");
	}

	/**
	 * called when app is resumed (from paused) and also
	 * the first time is executed (after start)
	 */
	@Override
	protected void onResume () {
		super.onResume();
		log("App Resumed");
	}

	/**
	 * called when app is paused
	 */
	@Override
	protected void onPause () {
		super.onPause();
		// We can control if the App is about to finish
		if (this.isFinishing()) {
			log("App is finishing");
		} else {
			log("App is NOT finishing");
		}

		log("App paused");
	}

	/**
	 * called when app is restarted from stop state
	 */
	@Override
	protected void onRestart () {
		super.onRestart();
		log("App Restarted");
	}

	/**
	 * called when app is stoped
	 */
	@Override
	protected void onStop () {
		super.onStop();
		log("App Stopped");
	}

	/**
	 * called when app is stoped is destroyed by Android
	 */
	@Override
	protected void onDestroy () {
		super.onDestroy();
		log("APP DESTROYED");
	}

	/**
	 * logs messages using Android log
	 * @param msg
	 */
	private void log (String msg) {
		Log.d("LifeCycle["+(counter++)+"]> "+msg,"PELLODEBUG");
	}
}