Ciclo de vida de la actividad
Las actividades son un bloque de creación fundamental de las aplicaciones Android y pueden existir en varios estados diferentes. El ciclo de vida de la actividad comienza con la creación de instancias y termina con la destrucción, e incluye muchos estados entre ellos. Cuando una actividad cambia de estado, se llama al método de evento de ciclo de vida adecuado, notificando a la actividad del cambio de estado inmediato y permitiéndole ejecutar código para adaptarse a ese cambio. En este artículo se examina el ciclo de vida de las actividades y se explica la responsabilidad que tiene una actividad durante cada uno de estos cambios de estado para formar parte de una aplicación confiable y bien comportada.
Información general sobre el ciclo de vida de la actividad
Las actividades son un concepto de programación inusual específico de Android. En el desarrollo de aplicaciones tradicional, normalmente hay un método main estático, que se ejecuta para iniciar la aplicación. Sin embargo, con Android, las cosas son diferentes; Las aplicaciones Android se pueden iniciar a través de cualquier actividad registrada dentro de una aplicación. En la práctica, la mayoría de las aplicaciones solo tendrán una actividad específica que se especifica como punto de entrada de la aplicación. Sin embargo, si una aplicación se bloquea o el sistema operativo finaliza, el sistema operativo puede intentar reiniciar la aplicación en la última actividad abierta o en cualquier otro lugar dentro de la pila de actividad anterior. Además, el sistema operativo puede pausar las actividades cuando no están activas y reclamarlas si tiene poca memoria. Debe tenerse en cuenta detenidamente para permitir que la aplicación restaure correctamente su estado en caso de que se reinicie una actividad, especialmente si esa actividad depende de los datos de actividades anteriores.
El ciclo de vida de la actividad se implementa como una colección de métodos a los que llama el sistema operativo a lo largo del ciclo de vida de una actividad. Estos métodos permiten a los desarrolladores implementar la funcionalidad necesaria para satisfacer los requisitos de administración de recursos y estados de sus aplicaciones.
Es muy importante que el desarrollador de aplicaciones analice los requisitos de cada actividad para determinar qué métodos expuestos por el ciclo de vida de la actividad deben implementarse. Si no lo hace, puede producir inestabilidad en la aplicación, bloqueos, un exceso de recursos e incluso la inestabilidad del sistema operativo subyacente.
En este capítulo se examina el ciclo de vida de la actividad en detalle, incluidos:
- Estados de actividad
- Métodos de ciclo de vida
- Conservar el estado de una aplicación
En esta sección también se incluye un tutorial que proporciona ejemplos prácticos sobre cómo guardar el estado de forma eficaz durante el ciclo de vida de la actividad. Al final de este capítulo, debe comprender el ciclo de vida de la actividad y cómo admitirlo en una aplicación Android.
Ciclo de vida de la actividad
El ciclo de vida de la actividad de Android consta de una colección de métodos expuestos dentro de la clase Activity que proporcionan al desarrollador un marco de administración de recursos. Este marco permite a los desarrolladores cumplir los requisitos de administración de estado únicos de cada actividad dentro de una aplicación y administrar correctamente la administración de recursos.
Estados de actividad
El sistema operativo Android aborda las actividades en función de su estado. Esto ayuda a Android a identificar las actividades que ya no están en uso, lo que permite que el sistema operativo reclame memoria y recursos. En el diagrama siguiente se muestran los estados por los que puede pasar una actividad durante su duración:
Estos estados se pueden dividir en 4 grupos principales como se indica a continuación:
Activo o En ejecución: las actividades se consideran activas o en ejecución si están en primer plano, también conocidas como la parte superior de la pila de actividad. Esto se considera la actividad de prioridad más alta en Android y, como tal, solo lo dará el sistema operativo en situaciones extremas, como si la actividad intenta usar más memoria de la que está disponible en el dispositivo, ya que esto podría hacer que la interfaz de usuario deje de responder.
Pausado: cuando el dispositivo entra en suspensión o una actividad sigue estando visible pero parcialmente oculta por una nueva actividad transparente o de tamaño no completo, la actividad se considera en pausa. Las actividades en pausa siguen activas, es decir, mantienen toda la información de estado y miembro, y permanecen asociadas al administrador de ventanas. Se considera la segunda actividad de prioridad más alta en Android y, como tal, solo la dará el sistema operativo si al acabar esta actividad se cumplirán los requisitos de recursos necesarios para mantener la actividad activa o en ejecución estable y con capacidad de respuesta.
Detenido/en segundo plano: las actividades que están completamente ocultas por otra actividad se consideran detenidas o en segundo plano. Las actividades detenidas siguen intentando conservar su estado y su información de miembro durante el mayor tiempo posible, pero las actividades detenidas se consideran la prioridad más baja de los tres estados y, por lo tanto, el sistema operativo elimina primero las actividades en este estado para satisfacer los requisitos de recursos de las actividades de prioridad más alta.
Reiniciado: Es posible que Android quite de la memoria una actividad que esté en cualquier lugar desde la pausa hasta la detenerse en el ciclo de vida. Si el usuario vuelve a la actividad, debe reiniciarse, restaurarse a su estado guardado previamente y, a continuación, mostrarse al usuario.
Actividad Re-Creation respuesta a los cambios de configuración
Para que las cosas más complicadas, Android lanza una llave más en la combinación denominada Cambios de configuración. Los cambios de configuración son ciclos rápidos de destrucción o re-creación de actividad que se producen cuando cambia la configuración de una actividad, como cuando se gira el dispositivo (y la actividad debe volver a crearse en modo horizontal o vertical), cuando se muestra el teclado (y se presenta la actividad con una oportunidad de cambiar su tamaño) o cuando el dispositivo se coloca en un dock, entre otros.
Los cambios de configuración siguen provocando los mismos cambios de estado de actividad que se producirían durante la detención y el reinicio de una actividad. Sin embargo, para asegurarse de que una aplicación tiene capacidad de respuesta y funciona bien durante los cambios de configuración, es importante que se controle lo más rápido posible. Por este problema, Android tiene una API específica que se puede usar para conservar el estado durante los cambios de configuración. Se trata más adelante en la sección Administración del estado a lo largo del ciclo de vida.
Métodos de ciclo de vida de actividad
El Android SDK y, por extensión, el marco de Xamarin.Android proporcionan un modelo eficaz para administrar el estado de las actividades dentro de una aplicación. Cuando cambia el estado de una actividad, el sistema operativo notifica a la actividad, que llama a métodos específicos de esa actividad. En el diagrama siguiente se muestran estos métodos en relación con el ciclo de vida de la actividad:
Como desarrollador, puede controlar los cambios de estado invalidando estos métodos dentro de una actividad. Sin embargo, es importante tener en cuenta que se llama a todos los métodos del ciclo de vida en el subproceso de la interfaz de usuario y bloqueará que el sistema operativo realice la siguiente parte del trabajo de la interfaz de usuario, como ocultar la actividad actual, mostrar una nueva actividad, etc. Por lo tanto, el código de estos métodos debe ser lo más breve posible para que una aplicación tenga un buen rendimiento. Las tareas de ejecución larga se deben ejecutar en un subproceso en segundo plano.
Examinemos cada uno de estos métodos de ciclo de vida y su uso:
OnCreate
OnCreate es el primer método al que se va a llamar cuando se crea una actividad.
OnCreate siempre se invalida para realizar cualquier inicialización de inicio que pueda ser necesaria para una actividad como:
- Creación de vistas
- Inicialización de variables
- Enlace de datos estáticos a listas
OnCreate toma un parámetro OnCreate que es un diccionario para almacenar y pasar información de estado y objetos entre actividades Si la agrupación no es NULL, esto indica que la actividad se está reiniciando y debe restaurar su estado desde la instancia anterior. En el código siguiente se muestra cómo recuperar valores de la agrupación:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
string intentString;
bool intentBool;
if (bundle != null)
{
intentString = bundle.GetString("myString");
intentBool = bundle.GetBoolean("myBool");
}
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
}
Una OnCreate vez que haya terminado, Android llamará a OnStart .
OnStart
El sistema siempre llama a OnStart una vez finalizado. Las actividades pueden invalidar este método si necesitan realizar tareas específicas justo antes de que una actividad se vuelva visible, como actualizar los valores actuales de las vistas dentro de la actividad. Android llamará OnResume inmediatamente después de este método.
OnResume
El sistema llama a OnResume cuando la actividad está lista para empezar a interactuar con el usuario. Las actividades deben invalidar este método para realizar tareas como:
- Aumentar las velocidades de fotogramas (una tarea común en el desarrollo de juegos)
- Inicio de animaciones
- Escucha de actualizaciones DE GPS
- Mostrar las alertas o cuadros de diálogo pertinentes
- Conexión de controladores de eventos externos
Por ejemplo, el siguiente fragmento de código muestra cómo inicializar la cámara:
protected override void OnResume()
{
base.OnResume(); // Always call the superclass first.
if (_camera==null)
{
// Do camera initializations here
}
}
OnResume es importante porque cualquier operación que se realiza en debe no realizarse en , ya que es el único método de ciclo de vida que se garantiza que se ejecute después de volver a dar vida OnPauseOnResume a la OnPause actividad.
OnPause
Se llama a OnPause cuando el sistema está a punto de poner la actividad en segundo plano o cuando la actividad se oculta parcialmente. Las actividades deben invalidar este método si necesitan:
Confirmación de cambios no guardados en datos persistentes
Destruir o limpiar otros objetos que consumen recursos
Velocidades de fotogramas de rampa y pausa de animaciones
Anule el registro de controladores de eventos externos o controladores de notificaciones (es decir, los que están asociados a un servicio). Esto debe hacerse para evitar pérdidas de memoria de actividad.
Del mismo modo, si la actividad ha mostrado diálogos o alertas, se deben limpiar con el
.Dismiss()método .
Por ejemplo, el siguiente fragmento de código liberará la cámara, ya que la actividad no puede hacer uso de ella mientras está en pausa:
protected override void OnPause()
{
base.OnPause(); // Always call the superclass first
// Release the camera as other activities might need it
if (_camera != null)
{
_camera.Release();
_camera = null;
}
}
Hay dos métodos de ciclo de vida posibles a los que se llamará después de OnPause :
OnResumeSe llamará a si la actividad se va a devolver al primer plano.OnStopSe llamará a si la actividad se coloca en segundo plano.
OnStop
Se llama a OnStop cuando la actividad ya no es visible para el usuario. Esto sucede cuando se produce una de las siguientes situaciones:
- Se está iniciando una nueva actividad que cubre esta actividad.
- Una actividad existente está en primer plano.
- La actividad se está destruyendo.
OnStop Es posible que no siempre se llame a en situaciones de poca memoria, como cuando Android se ha quedado sin recursos y no puede pasar correctamente a segundo plano la actividad. Por esta razón, es mejor no confiar en que se llame OnStop al preparar una actividad para la destrucción. Los siguientes métodos de ciclo de vida a los que se puede llamar después de este serán si la actividad va a desaparecer o si la actividad vuelve para interactuar OnDestroyOnRestart con el usuario.
OnDestroy
OnDestroy es el método final al que se llama en una instancia de Activity antes de que se destruya y se quitara completamente de la memoria. En situaciones extremas, Android puede acabar con el proceso de aplicación que hospeda la actividad, lo que provocará OnDestroy que no se invoque. La mayoría de las actividades no implementarán este método porque la mayoría de las actividades de limpieza y apagado se han realizado en los OnPauseOnStop métodos y . Normalmente, OnDestroy el método se invalida para limpiar las tareas de ejecución larga que podrían filtrar recursos. Un ejemplo de esto podría ser subprocesos en segundo plano que se iniciaron en OnCreate .
No se llamará a ningún método de ciclo de vida después de destruir la actividad.
OnRestart
Se llama a OnRestart después de que se haya detenido la actividad, antes de que se vuelva a iniciar. Un buen ejemplo de esto sería cuando el usuario presiona el botón inicio mientras está en una actividad de la aplicación. Cuando esto OnPause sucede, se llama OnStop a los métodos y la actividad se mueve al fondo, pero no se destruye. Si el usuario restaurara la aplicación mediante el administrador de tareas o una aplicación similar, Android llamará al OnRestart método de la actividad.
No hay ninguna guía general sobre qué tipo de lógica se debe implementar en OnRestart . Esto se debe a que siempre se invoca independientemente de si la actividad se está creando o reiniciando, por lo que los recursos requeridos por la actividad se deben inicializar en , en lugar OnStartOnStart de OnRestart .
El siguiente método de ciclo de vida al que se OnRestart llama después será OnStart .
Atrás frente a Inicio
Muchos dispositivos Android tienen dos botones distintos: un botón "Atrás" y un botón "Inicio". Puede ver un ejemplo de esto en la captura de pantalla siguiente de Android 4.0.3:
Hay una diferencia sutil entre los dos botones, aunque parezcan tener el mismo efecto que poner una aplicación en segundo plano. Cuando un usuario hace clic en botón Atrás, le dice a Android que ha terminado con la actividad. Android destruirá la actividad. Por el contrario, cuando el usuario hace clic en el botón Inicio, la actividad simplemente se coloca en segundo plano: Android no finalizará la actividad.
Administración del estado a lo largo del ciclo de vida
Cuando se detiene o destruye una actividad, el sistema ofrece la oportunidad de guardar el estado de la actividad para su posterior rehidratación. Este estado guardado se conoce como estado de instancia. Android proporciona tres opciones para almacenar el estado de la instancia durante el ciclo de vida de la actividad:
Almacenar valores primitivos en
Dictionaryun conocido como bundleDictionaryAndroid usará para guardar el estado.Crear una clase personalizada que contendrán valores complejos, como mapas de bits. Android usará esta clase personalizada para guardar el estado.
Evitar el ciclo de vida de cambio de configuración y asumir la responsabilidad total de mantener el estado en la actividad.
En esta guía se tratan las dos primeras opciones.
Estado de agrupación
La opción principal para guardar el estado de instancia es usar un objeto de diccionario de clave/valor conocido como Bundle.
Recuerde que cuando se crea una actividad que se pasa al método una agrupación como parámetro, esta agrupación se puede usar OnCreate para restaurar el estado de la instancia. No se recomienda usar una agrupación para datos más complejos que no se serializarán de forma rápida o sencilla en pares clave-valor (como mapas de bits). en su lugar, se debe usar para valores simples como cadenas.
Una actividad proporciona métodos para ayudar a guardar y recuperar el estado de instancia en la agrupación:
OnSaveInstanceState: Android lo invoca cuando se destruye la actividad. Las actividades pueden implementar este método si necesitan conservar los elementos de estado clave-valor.
OnRestoreInstanceState: se llama a este método una vez finalizado el método y proporciona otra oportunidad para que una actividad restaure su estado una vez completada la inicialización.
En el diagrama siguiente se muestra cómo se usan estos métodos:
OnSaveInstanceState
Se llamará a OnSaveInstanceState cuando se detenga la actividad. Recibirá un parámetro de agrupación en el que la actividad puede almacenar su estado. Cuando un dispositivo experimenta un cambio de configuración, una actividad puede usar el objeto que se pasa para conservar el estado de actividad Bundle invalidando OnSaveInstanceState . Por ejemplo, considere el siguiente código:
int c;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
this.SetContentView (Resource.Layout.SimpleStateView);
var output = this.FindViewById<TextView> (Resource.Id.outputText);
if (bundle != null) {
c = bundle.GetInt ("counter", -1);
} else {
c = -1;
}
output.Text = c.ToString ();
var incrementCounter = this.FindViewById<Button> (Resource.Id.incrementCounter);
incrementCounter.Click += (s,e) => {
output.Text = (++c).ToString();
};
}
El código anterior incrementa un entero denominado cuando se hace clic en un botón denominado c , mostrando el resultado en un denominado incrementCounterTextViewoutput . Cuando se produce un cambio de configuración (por ejemplo, cuando se gira el dispositivo), el código anterior perdería el valor de porque sería , como se muestra en la cbundle ilustración null siguiente:
Para conservar el valor de en este ejemplo, la actividad puede invalidar , guardando c el valor en la OnSaveInstanceState agrupación como se muestra a continuación:
protected override void OnSaveInstanceState (Bundle outState)
{
outState.PutInt ("counter", c);
base.OnSaveInstanceState (outState);
}
Ahora, cuando el dispositivo gira a una nueva orientación, el entero se guarda en la agrupación y se recupera con la línea :
c = bundle.GetInt ("counter", -1);
Nota:
Es importante llamar siempre a la implementación base de para que también se pueda guardar el estado de la jerarquía OnSaveInstanceState de vistas.
Estado de vista
La invalidación es un mecanismo adecuado para guardar datos transitorios en una actividad en los cambios de orientación, como el contador OnSaveInstanceState del ejemplo anterior. Sin embargo, la implementación predeterminada de se encarga de guardar datos transitorios en la interfaz de usuario para cada vista, siempre y cuando cada vista OnSaveInstanceState tenga asignado un identificador. Por ejemplo, diga que una aplicación tiene EditText un elemento definido en XML de la siguiente manera:
<EditText android:id="@+id/myText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Puesto que el control tiene un asignado, cuando el usuario escribe algunos datos y gira el dispositivo, los datos se siguen mostrados, como se EditTextid muestra a continuación:
OnRestoreInstanceState
Se llamará a OnRestoreInstanceState después de . Proporciona a una actividad la oportunidad de restaurar cualquier estado que se guardó previamente en una agrupación durante el OnSaveInstanceState anterior. Sin embargo, se trata de la misma agrupación que se proporciona OnCreate a .
El código siguiente muestra cómo se puede restaurar el estado en OnRestoreInstanceState :
protected override void OnRestoreInstanceState(Bundle savedState)
{
base.OnRestoreInstanceState(savedState);
var myString = savedState.GetString("myString");
var myBool = savedState.GetBoolean("myBool");
}
Este método existe para proporcionar cierta flexibilidad en torno al momento en que se debe restaurar el estado. A veces es más adecuado esperar hasta que se realizan todas las inicializaciones antes de restaurar el estado de la instancia. Además, es posible que una subclase de una actividad existente solo desee restaurar determinados valores del estado de instancia. En muchos casos, no es necesario invalidar , ya que la mayoría de las actividades pueden restaurar el estado OnRestoreInstanceState mediante la agrupación proporcionada a OnCreate .
Para obtener un ejemplo de cómo guardar el estado mediante Bundle , consulte Bundle.
Limitaciones de Bundle
Aunque OnSaveInstanceState facilita el guardado de datos transitorios, tiene algunas limitaciones:
No se llama en todos los casos. Por ejemplo, si presiona Inicio o Atrás para salir de una actividad, no se llamará a .
La agrupación que
OnSaveInstanceStatese pasa a no está diseñada para objetos grandes, como imágenes. En el caso de objetos grandes, es preferible guardar el objeto desde OnInstanceinNonConfigurationInstance, como se describe a continuación.Los datos guardados mediante la agrupación se serializan, lo que puede provocar retrasos.
El estado de agrupación es útil para datos simples que no usan mucha memoria, mientras que los datos de instancia que no son de configuración son útiles para datos más complejos o datos que son caros de recuperar, como una llamada de servicio web o una consulta de base de datos complicada. Los datos de instancia que no son de configuración se guardan en un objeto según sea necesario. En la sección siguiente se presenta como una manera de conservar los tipos de datos más complejos mediante OnRetainNonConfigurationInstance cambios de configuración.
Conservación de datos complejos
Además de conservar los datos en la agrupación, Android también admite el almacenamiento de datos invalidando OnInstanceinNonConfigurationInstance y devolviendo una instancia de que contiene los datos que se van a conservar. Hay dos ventajas principales de usar OnRetainNonConfigurationInstance para guardar el estado:
El objeto devuelto de funciona bien con tipos de datos más grandes y
OnRetainNonConfigurationInstancecomplejos porque la memoria conserva este objeto.Se
OnRetainNonConfigurationInstancellama al método a petición y solo cuando es necesario. Esto es más económico que usar una caché manual.
El uso de es adecuado para escenarios en los que resulta costoso recuperar los datos varias veces, como en OnRetainNonConfigurationInstance las llamadas de servicio web. Por ejemplo, considere el siguiente código que busca en Twitter:
public class NonConfigInstanceActivity : ListActivity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SearchTwitter ("xamarin");
}
public void SearchTwitter (string text)
{
string searchUrl = String.Format("http://search.twitter.com/search.json?" + "q={0}&rpp=10&include_entities=false&" + "result_type=mixed", text);
var httpReq = (HttpWebRequest)HttpWebRequest.Create (new Uri (searchUrl));
httpReq.BeginGetResponse (new AsyncCallback (ResponseCallback), httpReq);
}
void ResponseCallback (IAsyncResult ar)
{
var httpReq = (HttpWebRequest)ar.AsyncState;
using (var httpRes = (HttpWebResponse)httpReq.EndGetResponse (ar)) {
ParseResults (httpRes);
}
}
void ParseResults (HttpWebResponse httpRes)
{
var s = httpRes.GetResponseStream ();
var j = (JsonObject)JsonObject.Load (s);
var results = (from result in (JsonArray)j ["results"] let jResult = result as JsonObject select jResult ["text"].ToString ()).ToArray ();
RunOnUiThread (() => {
PopulateTweetList (results);
});
}
void PopulateTweetList (string[] results)
{
ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.ItemView, results);
}
}
Este código recupera los resultados de la web con formato JSON, los analiza y, a continuación, presenta los resultados en una lista, como se muestra en la captura de pantalla siguiente:
Cuando se produce un cambio de configuración (por ejemplo, cuando se gira un dispositivo), el código repite el proceso. Para reutilizar los resultados recuperados originalmente y no provocar llamadas de red innecesarias y redundantes, podemos usar para guardar los resultados, como se OnRetainNonconfigurationInstance muestra a continuación:
public class NonConfigInstanceActivity : ListActivity
{
TweetListWrapper _savedInstance;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
var tweetsWrapper = LastNonConfigurationInstance as TweetListWrapper;
if (tweetsWrapper != null) {
PopulateTweetList (tweetsWrapper.Tweets);
} else {
SearchTwitter ("xamarin");
}
public override Java.Lang.Object OnRetainNonConfigurationInstance ()
{
base.OnRetainNonConfigurationInstance ();
return _savedInstance;
}
...
void PopulateTweetList (string[] results)
{
ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.ItemView, results);
_savedInstance = new TweetListWrapper{Tweets=results};
}
}
Ahora, cuando se gira el dispositivo, los resultados originales se recuperan de la LastNonConfiguartionInstance propiedad . En este ejemplo, los resultados constan de un string[] tweet que contiene. Puesto que requiere que se devuelva un , se encapsula en una clase que OnRetainNonConfigurationInstanceJava.Lang.Objectstring[] subclases Java.Lang.Object , como se muestra a continuación:
class TweetListWrapper : Java.Lang.Object
{
public string[] Tweets { get; set; }
}
Por ejemplo, al intentar usar como el objeto devuelto de se pierde la actividad, como se TextView muestra en el código OnRetainNonConfigurationInstance siguiente:
TextView _textView;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
var tv = LastNonConfigurationInstance as TextViewWrapper;
if(tv != null) {
_textView = tv;
var parent = _textView.Parent as FrameLayout;
parent.RemoveView(_textView);
} else {
_textView = new TextView (this);
_textView.Text = "This will leak.";
}
SetContentView (_textView);
}
public override Java.Lang.Object OnRetainNonConfigurationInstance ()
{
base.OnRetainNonConfigurationInstance ();
return _textView;
}
En esta sección, hemos aprendido a conservar datos de estado simples con Bundle y a conservar tipos de datos más complejos con OnRetainNonConfigurationInstance .
Resumen
El ciclo de vida de la actividad de Android proporciona un marco eficaz para la administración de estados de las actividades dentro de una aplicación, pero puede ser difícil de entender e implementar. En este capítulo se presentan los distintos estados por los que puede pasar una actividad durante su duración, así como los métodos de ciclo de vida asociados a esos estados. A continuación, se proporcionó orientación sobre qué tipo de lógica se debe realizar en cada uno de estos métodos.






