Diferencia entre revisiones de «Ciclo de vida dunha aplicación»

De MediaWiki
Ir a la navegación Ir a la búsqueda
(Página blanqueada)
Etiqueta: Vaciado
 
(No se muestran 11 ediciones intermedias del mismo usuario)
Línea 1: Línea 1:
  
==Introdución==
 
*Unha '''Activity (Actividade)''' é un elemento de Android que amosa unha pantalla con elementos (Vistas: botóns, textos, imaxes, etc) cos que os usuarios poden interactuar: chamar por teléfono, navegar por internet, realizar cálculos nunha calculadora.
 
*Unha actividade, xeralmente, ocupa toda a pantalla, aínda que pode ser menor ca esta ou flotar sobre outras ventás.
 
*Xeralmente, defínese unha actividade como '''principal''' no '''AndroidManifest.xml''', pola cal se inicia a aplicación. (Como a función/método main() noutras linguaxes de programación).
 
*Cada aplicación pode ter varias actividades.
 
*Ademais, dende unha Actividade dunha aplicación pódese saltar á actividade doutra aplicación (P.E. dende WhatsApp podemos saltar a consultar os datos dun contacto da aplicación contactos).
 
*Cando iniciamos unha actividade, esta pasa ao '''primeiro plano''' ('''Visible''') e a actividade anterior detense e envíase xusto para detrás da actual na Pila ('''Back stack''').
 
*Esta pila usa o mecanismo de colas LIFO. Cando prememos a tecla de retroceso no móbil destrúese a actividade actual e recárgase á actividade que está no top da pila.
 
 
*Dende que iniciamos unha actividade até que saímos dela, esta pasa por distintos estados: '''O ciclo de vida dunha actividade''':
 
[[Image:00_basic-lifecycle.png|500px]].
 
 
 
*'''Referencias''':
 
**Actividades: http://developer.android.com/guide/components/activities.html
 
**O ciclo de vida dunha actividade: http://developer.android.com/training/basics/activity-lifecycle/index.html
 
 
==Ciclo de vida dunha actividade==
 
*Cando unha actividade cambia de estado (porque se preme unha tecla do teclado, porque se preme un botón, etc) este cambio é notificado á actividade a través dos métodos '''callback'''.
 
*Todos estes métodos '''callback''' capturan os cambios de estado que se van producindo na actividade e poden ser sobreescritos para que realicen as operacións que desexemos.
 
 
[[Image:00_basic-lifecycle.png]].
 
 
*Observar a imaxe, observar como ten forma de pirámide, dende o estado no que se '''lanza''' unha actividade até que chega ao estado '''Resumed (Running)''', esta pasa polos estados '''Created''' e '''Started'''.
 
 
 
*Cada un deses cambios de estado da actividade ten asociado un método ''callback'' que será chamado no momento de producirse o cambio. Por orde: '''onCreate()''', '''onStart()''', '''onResume()'''
 
 
 
*Unha actividade estará no estado '''Paused''' se esta está '''semi-visible''' porque hai outra actividade en primeiro plano por enriba da primeira que non ocupa toda a pantalla.
 
**Neste cambio chamarase ao método '''onPause()'''.
 
**De este estado pódese pasar á '''Running''' e o método '''onResume()''' será o chamado nese cambio de estado.
 
 
 
*Unha actividade pasa ao estado '''Stopped''' cando pasa a segundo plano, cando non está visible, ben porque se abriu unha nova actividade ou porque se premeu o botón '''Home''' do móbil. Se pasamos do estado '''Resumed''' a '''Stopped''' estes cambios son capturados polos métodos '''onPause()''' e '''onStop()'''.
 
**Unha actividade pode pasar de '''Stopped''' a '''Resumend''', pasando por '''Started''', porque se volve a pasar a primeiro plano a actividade que antes estaba oculta na pila de actividades, neste caso dous métodos capturan os cambios: '''onRestart()''' e de novo '''onStart()''' e '''onResume()'''.
 
**Mentres unha actividade está no estado '''Stopped''' retéñense todas as súas variables, información de estado e recursos que está usando.
 
 
 
*Unha actividade pasa ao estado '''Destroyed''' nos seguintes casos:
 
**A actividade está en primeiro plano (Resumed) e prémese o botón '''Back''', neste caso pasamos de '''Resumed''' a '''Destroyed''', pasando por '''Paused''' e '''Stopped''' chamándose a tódolos métodos que hai polo camiño.
 
***Se había unha actividade en segundo plano (que agora estará no estado Stopped) antes de destruír a actual, esa será traída a primeiro plano pasando de '''Stopped''' a '''Resumed'''.
 
**A Actividade ten programado no seu código que se destrúa explicitamente co método '''finish()''', por exemplo ao premer un botón de saír.
 
**A Actividade está no fondo da pila de actividades (Stopped) e o sistema precisa os seus recursos para poder asignarllos a unha nova actividade que o usuario quere abrir. Neste caso, o sistema destrúe esa actividade e por tanto esta perde todo aquelo que non fose gardado antes de pasar ao estado de '''Stopped'''.
 
**Dende o '''administrador de aplicacións'''.
 
 
 
 
*'''IMPORTANTE''': cando se '''xira o dispositivo''' de vertical a horizontal (e viceversa) e hai unha actividade en primeiro plano esta destrúese e vólvese a lanzar, con todo o que iso implica: Perderanse os valores das vistas salvo que estas teñan creado un ID: '''android:id="@+id/...'''
 
 
 
*Dependendo do que desexemos realizar é probable que non desexemos implementar todos os métodos do ciclo de vida.
 
*Coñecendo o seu funcionamento poderemos evitar:
 
**Un colgue da aplicación se por exemplo recibimos unha chamada.
 
**Non realizar un consumo excesivo de recursos se a actividade non está activa.
 
**Non perder datos de usuario se este sae da aplicación e logo volve a ela.
 
**Non perder datos de usuario se este cambia a orientación do dispositivo.
 
 
 
 
===Caso práctico 1===
 
* Partimos que xa temos creado o proxecto inicial como [http://wiki.cifprodolfoucha.es/index.php?title=PDM_Creando_proxecto_base xa indicamos anteriormente].
 
: Se non o temos creado antes, crea un paquete de nome '''CicloVida''' como un subpaquete do teu paquete principal.
 
 
<br />
 
* Dentro do paquete '''CicloVida''' crear unha nova 'Empty Activity' de nome: '''UD03_01_CicloVida''' de tipo Launcher e sen compatibilidade.
 
: Modifica o arquivo '''AndroidManifiest.xml''' e engade unha label á activity como [http://wiki.cifprodolfoucha.es/index.php?title=PDM_Creando_proxecto_base xa vimos na creación do proxecto base].
 
 
 
<br />
 
 
**Este proxecto está baseado nun proxecto do curso de Android da Aula Mentor (http://www.mentor.mec.es/)
 
*Nesta ocasión vanse implantar os 7 métodos anteriores e realizar tarefas no dispositivo: premer un botón, premer as teclas Atrás e Home, volver a lanzar a Actividade, xirar o dispositivo etc.
 
*Entre outras cousas imos ver se perde información ou non mentres a actividade está en segundo plano na pila.
 
*Cada método terá un Toast que indicará que foi chamado ese método callback.
 
 
 
*A Actividade so ten:
 
**Un botón para finalizar a actividade.
 
**Unha caixa de texto no que poder escribir e comprobar no futuro se o escrito se conserva.
 
**Unha etiqueta explicando o que fai a actividade.
 
[[Image:Android_2013_U3_01_LifeCycle_01.jpg]]
 
 
 
 
*Escribimos algo na caixa de texto.
 
[[Image:Android_2013_U3_01_LifeCycle_02.jpg]]
 
*Se prememos, por exemplo, na tecla "Home" vemos cales son os estados polos que pasa a actividade e consecuentemente os métodos callback que son chamados.
 
 
 
*A imaxe amosa como a actividade está indo para o segundo plano, non está visible, está oculta.
 
*Os métodos que foron chamados nese proceso son: onPause(), onStop()
 
*A actividade estará no estado '''Stopped'''
 
[[Image:Android_2013_U3_01_LifeCycle_03.jpg]]
 
 
 
*Agora se volvemos lanzar a actividade, esta pasara do estado '''Stopped''' a '''Resumed'''.
 
*Polo tanto, vanse chamar os métodos: onRestart(), onStart(), onResume()
 
*Finalmente a actividade non perdeu información.
 
[[Image:Android_2013_U3_01_LifeCycle_04.jpg]]
 
 
 
*O usuario pode probar agora a premer a tecla '''Atras''', o botón '''Finalizar''' e xirar o dispositivo e comprobar se se conserva ou non a información: (Logo o resolveremos)
 
 
 
====Parando e reiniciando unha aplicación====
 
*O proceso que se recolle no exemplo anterior recóllese no seguinte esquema:
 
[[Image:00_basic-lifecycle-stopped.png]]
 
 
===Ficheiros XML: Layout e strings===
 
 
<syntaxhighlight lang="xml" line enclose="div" highlight="18" >
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 
    android:layout_width="match_parent"
 
    android:layout_height="match_parent"
 
    android:orientation="vertical" >
 
 
    <LinearLayout
 
        android:layout_width="match_parent"
 
        android:layout_height="wrap_content"
 
        android:orientation="horizontal" >
 
 
        <Button
 
            android:layout_width="wrap_content"
 
            android:layout_height="wrap_content"
 
            android:onClick="onFinalizarClick"
 
            android:text="Finalizar" />
 
 
        <EditText
 
            android:id="@+id/et"
 
            android:layout_width="wrap_content"
 
            android:layout_height="wrap_content"
 
            android:hint="Texto" />
 
    </LinearLayout>
 
 
    <ScrollView
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content" >
 
 
        <TextView
 
            android:layout_width="match_parent"
 
            android:layout_height="wrap_content"
 
            android:text="@string/instrucions" />
 
    </ScrollView>
 
 
</LinearLayout>
 
</syntaxhighlight>
 
*'''Liña 18''': Grazas a que se crea un ID no EditText, o seu contido non se perderá cando cando se xire o dispositivo.
 
*Probar a eliminar ese atributo (id) do XML e volver compilar a aplicación. Escribir no EditText e xirar a pantalla. Que pasa?.
 
 
 
*'''O Ficheiro /res/values/strings.xml'''
 
<syntaxhighlight lang="xml" line enclose="div" highlight="1" >
 
<?xml version="1.0" encoding="utf-8"?>
 
<resources>
 
 
    <string name="app_name">U3_01_LifeCycle</string>
 
    <string name="action_settings">Settings</string>
 
    <string name="instrucions">Ciclo de vida:\n\n
 
1. Ao iniciar á actividade lánzanse os métodos: onCreate, onStart e
 
onResume.\n\n
 
 
2. Se fas click no botón Atrás lánzanse os métodos
 
: onPause, onStop e onDestroy.\n\n
 
 
3. Cando se preme Finalizar os métodos: onPause, onStop e
 
onDestroy.\n\n
 
 
4. A facer click Home lánzanse os métodos: onPause e onStop.\n\n
 
 
5. Se volvemos lanzar a actividade en espera lánzanse os métodos:
 
onRestart, onStart e onResume)\n\n
 
 
6.- Se xiras a pantalla o valor do EditText Texto conservarase
 
se este ten asignado un ID ou se garda o seu estado.
 
</string>
 
</resources>
 
 
</syntaxhighlight>
 
 
===O código Java===
 
 
<syntaxhighlight lang="java" line enclose="div" highlight="13,14,56" >
 
package com.example.u3_01_lifecycle;
 
 
import android.app.Activity;
 
import android.os.Bundle;
 
import android.view.Menu;
 
import android.view.View;
 
import android.widget.Toast;
 
 
public class U3_01_LifeCycle extends Activity {
 
 
Bundle dato = new Bundle();
 
 
public void onCreate(Bundle savedInstanceState) {
 
super.onCreate(savedInstanceState);
 
setContentView(R.layout.activity_u3_01__life_cycle);
 
Toast.makeText(this, "Execútase: onCreate. Aproveitar para recuperar info da última sesión", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onStart() {
 
super.onStart();
 
Toast.makeText(this, "Execútase: onStart", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onResume() {
 
super.onResume();
 
Toast.makeText(this, "Execútase: onResume", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onPause() {
 
super.onPause();
 
Toast.makeText(this, "Execútase: onPause. Aproveitar para gardar información por se se destrúe", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onRestart() {
 
super.onRestart();
 
Toast.makeText(this, "Execútase: onRestart", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onStop() {
 
super.onStop();
 
Toast.makeText(this, "Execútase: onStop", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onDestroy() {
 
super.onDestroy();
 
Toast.makeText(this, "Execútase: onDestroy. Saímos", Toast.LENGTH_SHORT).show();
 
}
 
 
public void onFinalizarClick(View v) {
 
finish();
 
}
 
 
@Override
 
public boolean onCreateOptionsMenu(Menu menu) {
 
// Inflate the menu; this adds items to the action bar if it is present.
 
getMenuInflater().inflate(R.menu.u3_01__life_cycle, menu);
 
return true;
 
}
 
 
}
 
 
</syntaxhighlight>
 
*Os métodos callback están sobreescritos e chaman antes de nada aos métodos do pai.
 
*'''Liñas 13,14''': Recíbese un '''Bundle''' (Conxunto de pares <parámetro, valor> ou null) e cárganse os seus valores nas vistas correspondente. Logo verase máis a fondo.
 
*'''Liñas 55,56''': Ao executar o método '''finish()''' vaise pechar a actividade. Esta pasará polos estados correspondentes dende '''Resumed''' até '''Destroyed'''.
 
 
 
 
'''EXERCICIOS:'''
 
*Tiñamos unha cuestión pendente:''O usuario pode probar agora a premer a tecla '''Atras''', o botón '''Finalizar''' e '''xirar o dispositivo''' e comprobar se se conserva ou non a información'':
 
**A conclusión debera ser que nos dous primeiros casos non se conserva a información da caixa de Texto, porque se destrúe a aplicación, pero ...
 
**Ao xirar o dispositivo tamén se destrúe a actividade e iniciase dende cero.
 
***Pero o sistema garda automaticamente nun Bundle (Parámetro, Valor), para cada vista da Actividade, que teña creado un identificador "@+id/", o seu estado.
 
***Ese Bundle é recuperado no método onCreate cando se inicia de novo a actividade.
 
***Probar a eliminar a liña 18 do ficheiro XML do layout e realizar as probas oportunas. Que pasa co contido do EditText?
 
 
===Estado Paused===
 
*Para afondar no anterior e presentar o estado '''Paused''' o usuario debe baixar este proxecto de Android: http://developer.android.com/shareables/training/ActivityLifecycle.zip e importalo.
 
*Comprobar os distintos estados polos que pode pasar/quedar unha actividade. Entre eles que unha actividade estea parcialmente visible, isto é '''Paused'''.
 
*A aplicación ten 3 Activities (A, B, C) que se verá despois como se crean varias Actividades nunha aplicación e como se lanzan. A aplicación tamén ten un cadro de diálogo, para poder ver como unha Activity está '''parcialmente-visible'''.
 
 
 
<gallery caption="Algunhas probas coa aplicación" widths="350" heights="350px" perrow="2">
 
Imagen:Android_2013_U3_Lifecycle_01.jpg| Ao lanzar a aplicación a primeira Activity que se lanza é a '''A'''. Observar que está no estatus '''Resumed''', pero antes pasou por '''Created''' e '''Started'''
 
Imagen:Android_2013_U3_Lifecycle_02.jpg| Se lanzamos a activity '''B''', premendo no botón '''Start B''', observar que a activity A, pasa polos estados '''Paused''' e '''Stopped''', e, entre medias, lánzase a activity B. Observar que a activity A quédase no estado '''Stopped'''
 
Imagen:Android_2013_U3_Lifecycle_03.jpg| Se prememos no botón '''Dialog''' observar que se lanza un cadro de diálogo e por detrás queda a Activity B '''parcialmente visible''' e por tanto no estado '''Paused'''.
 
Imagen:Android_2013_U3_Lifecycle_04.jpg|Ao pechar o cadro de diálogo pódese observar que a Activity B pasou de '''Paused''' a '''Resumed'''.
 
Imagen:Android_2013_U3_Lifecycle_05.jpg| Se pechamos a activity B, volvemos á activity que estaba na cola LIFO: a activity A. Vemos que agora a activity A está no estado '''Resumed''' pero antes pasou por '''Restarted''' e '''Started'''.<br>Observar, como paralelamente, a activity B, pasou do estado '''Resumed''' a '''Destroyed''', pasando por '''Paused''' e '''Stopped'''.
 
</gallery>
 
 
 
*O usuario pode experimentar máis con esta aplicación para familiarizarse co ciclo de vida dunha activity, ollo dunha activity, non dunha aplicación. Esta última está composta por varias activities, como mínimo unha.
 
 
 
*O seguinte diagrama recolle esta situación:
 
[[Image:00_basic-lifecycle-paused.png]]
 
 
*Cando se está neste estado  ('''Paused''') é cando se deben pasar os datos á BBDD, gardar as preferencias, etc, porque a partir de aquí o sistema non garante que sempre poida pasar polos estados '''Stopped''' e '''Destroyed'''.
 
 
==Recrear unha actividade: gardar e recuperar o seu estado==
 
*Existen varias casos nos que unha actividade pode ser destruída:
 
**Premendo o botón Atrás,
 
**Chamando a finish()
 
**Xirando o dispositivo
 
**Estando en segundo plano e o sistema precise a súa memoria e a destrúe
 
 
 
*Os 2 primeiros casos entendese que foron propiciados polo desexo do usuario.
 
*Os 2 últimos casos, poden ser accidentais (podese xirar o dispositivo sen querer ou precísase a memoria do dispositivo). Neste caso cando se relance de novo a actividade ('''Recrear a actividade''') gustaríanos que a aplicación conservara o seu estado.
 
*Nestes casos, se o usuario quere volver á actividade, o sistema pode recuperar a información de estado dunha instancia anterior.
 
*Para iso o sistema antes de destruír a actividade garda o estado da aplicación: '''instance state (Estado de instancia)'''
 
*Ese estado está gardado nun obxecto da clase '''Bundle''' que garda pares '''clave-valor'''
 
*Unha vez que se lanza de novo a actividade (recrea), esta recupera o seu estado de instancia.
 
 
[[Image:00_basic-lifecycle-savestate.png]]
 
*Cando o sistema comeza a parar a actividade chama ao método '''onSaveInstanceState()''' (Paso 1)
 
***Podemos sobreescribir o método e aproveitar para gardar a información que desexemos nun obxecto Bundle.
 
*Cando se recrea a actividade o sistema páselle o Bundle a 2 métodos: onCreate() e '''onRestoreInstanceState()''' e podemos aproveitar para recuperar a información previamente gardada.
 
 
 
* Seguramente a aplicación ten outra información aparte do estado das View´s que é necesario recuperar, como as variables usadas para gardar o progreso do usuario ou calquera outra información que nos como programadores queiramos gardar.
 
 
: Para iso existe o método onSaveInstanceState que chama o sistema cando a aplicación pasa a segundo plano ou se destrúe.
 
[[Imagen:PDM_Activity_16.jpg|400px|center]]
 
 
 
: A este método lle pasa o obxecto da [https://developer.android.com/reference/android/os/Bundle?hl=es-419 clase Bundle] onde está gardada toda a información (view´s) da aplicación, polo que o programador pode engadir máis información para ser recuperada posteriormente se o sistema volta a chamar a aplicación, xa que lle volve a pasar o mesmo obxecto da clase Bundle.
 
: O obxecto da clase Bundle funciona en base a pares '''CLAVE-VALOR'''. A clave e o identificar do valor que se vai pasar. Ven ser como o nome dunha variable en programación.
 
: Así teremos:
 
:* método '''putXXXXX(CLAVE,VALOR)''' sendo XXXXX un tipo de dato, por exemplo, putInt(), para engadir datos ao obxecto Bundle.
 
:* método '''getXXXXX(CLAVE)''''  sendo XXXXX un tipo de dato, por exemplo, getInt(), par recuperar o valor dun elemento clave gardado.
 
 
: Por exemplo, se quero gardar unha cadea nunha 'clave' de nome 'LOGIN', poñería:
 
:: obxectoBundle.putString("LOGIN","CADEA A GARDAR");
 
 
: Se quere recuperar dito valor, no mesmo obxecto bundle:
 
:: String login = obxectoBundle.getString("LOGIN");
 
 
 
: Loxicamente o nome da clave ten que ser a mesma cando gardamos o dato e cando o introducimos.
 
 
 
 
* Cando a aplicación se re-inicializa (isto é, cando se rota o dispositivo ou volvemos a aplicación unha vez que o S.O. liberou a súa memoria por necesidade) se chama ao '''método onRestoreInstanceState''', onde se recuperan os datos gardados.
 
 
: <u>Nota:</u> Antes de chegar o estado Resumed pasa polo estado Started.
 
 
 
* Exemplo:
 
<syntaxhighlight lang="java" line highlight="">
 
@Override
 
public void onSaveInstanceState(Bundle datosgardados){
 
 
datosgardados.putInt(“POSICION”,reproductor.getPosicionActualCancion());
 
datosgardados.putString(“CANCION”,reproductor.cancionactual);
 
 
// SEMPRE O CODIGO POSTO POR NOS ANTES DA CHAMADA A SUPER.
 
super.onSaveInstanceState(datosgardados);
 
}
 
</syntaxhighlight>
 
 
 
: Para restaurar o sistema usaremos o método onRestoreInstanceState.
 
<syntaxhighlight lang="java" line highlight="">
 
@Override
 
public void onRestoreInstanceState(Bundle datosgardados){
 
 
super.onRestoreInstanceState(datosgardados);
 
 
// CODIGO POSTO POR NOS PARA RECUPERAR OS DATOS, SEMPRE DESPOIS
 
// DA CHAMADA A SUPER
 
reproductor.cancionactual=datosgardados.getString(C_DATOCANCION_PECHAR);
 
reproductor.posicioncancion=datosgardados.getInt(C_DATOPOSCANCION_PECHAR);
 
 
}
 
 
</syntaxhighlight>
 
 
:<u>Nota:</u> É importante chamar os métodos super.XXXXX na orde indicada no exemplo anterior.
 
 
 
:<u>Nota:</u> Podemos facer que Android non garde o estado dun view poñendo como atributo: <b>android:saveEnabled="false"</b> no arquivo XML do layout ou por programación.
 
 
 
<br />
 
*'''Referencias''':
 
**http://developer.android.com/training/basics/activity-lifecycle/recreating.html
 
 
 
<br />
 
===Caso Práctico 1===
 
 
* Partimos que xa temos creado o proxecto inicial como [http://wiki.cifprodolfoucha.es/index.php?title=PDM_Creando_proxecto_base xa indicamos anteriormente].
 
: Se non o temos creado antes, crea un paquete de nome '''UI''' como un subpaquete do teu paquete principal.
 
: Dentro do paquete UI, crearemos un novo paquete de nome: '''gardandoestado'''.
 
 
 
<br />
 
 
* Imos ver un exemplo práctico disto.
 
: Dentro do paquete imos crear unha nova 'Empty Activity' de nome '''UD2_01_gardando_estado''' de tipo Launcher e sen compatibilidade.
 
: Modifica o arquivo '''AndroidManifiest.xml''' e engade unha label á activity como [http://wiki.cifprodolfoucha.es/index.php?title=PDM_Creando_proxecto_base xa vimos na creación do proxecto base].
 
 
 
: Cambia o contido do arquivo do Layout XML por este:
 
<syntaxhighlight lang="java" line highlight="">
 
<?xml version="1.0" encoding="utf-8"?>
 
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 
    xmlns:app="http://schemas.android.com/apk/res-auto"
 
    xmlns:tools="http://schemas.android.com/tools"
 
    android:layout_width="match_parent"
 
    android:layout_height="match_parent"
 
    tools:context=".gardandoestado.UD2_01_gardando_estado">
 
 
 
    <EditText
 
        android:id="@+id/editText"
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content"
 
        android:layout_marginBottom="8dp"
 
        android:layout_marginEnd="8dp"
 
        android:layout_marginStart="8dp"
 
        android:layout_marginTop="8dp"
 
        android:ems="10"
 
        android:inputType="textPersonName"
 
        android:text="Name"
 
        app:layout_constraintBottom_toBottomOf="parent"
 
        app:layout_constraintEnd_toEndOf="parent"
 
        app:layout_constraintStart_toStartOf="parent"
 
        app:layout_constraintTop_toTopOf="parent"
 
        app:layout_constraintVertical_bias="0.073" />
 
 
    <Button
 
        android:id="@+id/button1"
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content"
 
        android:layout_marginEnd="8dp"
 
        android:layout_marginStart="8dp"
 
        android:layout_marginTop="84dp"
 
        android:text="Dar Valor"
 
        app:layout_constraintEnd_toEndOf="parent"
 
        app:layout_constraintStart_toStartOf="parent"
 
        app:layout_constraintTop_toBottomOf="@+id/editText" />
 
 
    <Button
 
        android:id="@+id/button2"
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content"
 
        android:layout_marginEnd="8dp"
 
        android:layout_marginStart="8dp"
 
        android:layout_marginTop="48dp"
 
        android:text="Ver valor"
 
        app:layout_constraintEnd_toEndOf="parent"
 
        app:layout_constraintStart_toStartOf="parent"
 
        app:layout_constraintTop_toBottomOf="@+id/button1" />
 
 
</android.support.constraint.ConstraintLayout>
 
</syntaxhighlight>
 
 
 
* Cambio o contido da activity por este:
 
<syntaxhighlight lang="java" line highlight="">
 
package es.cursoandroid.cifprodolfoucha.aprendiendo.gardandoestado;
 
 
import android.app.Activity;
 
import android.os.Bundle;
 
import android.view.View;
 
import android.widget.Button;
 
import android.widget.Toast;
 
 
import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
 
 
 
public class UD2_01_gardando_estado extends Activity {
 
 
    private int valor = 0;
 
 
    @Override
 
    protected void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
 
        setContentView(R.layout.activity_ud2_01_gardando_estado);
 
 
        Button btnBoton1 = findViewById(R.id.button1);
 
        btnBoton1.setOnClickListener(new View.OnClickListener() {
 
            @Override
 
            public void onClick(View v) {
 
                valor = 5;
 
                Toast.makeText(getApplicationContext(), "Valor: " + valor, Toast.LENGTH_LONG).show();
 
            }
 
        });
 
 
        Button btnBoton2 = findViewById(R.id.button2);
 
        btnBoton2.setOnClickListener(new View.OnClickListener() {
 
            @Override
 
            public void onClick(View v) {
 
                Toast.makeText(getApplicationContext(), "Valor: " + String.valueOf(valor), Toast.LENGTH_LONG).show();
 
            }
 
        });
 
 
 
    }
 
}
 
 
</syntaxhighlight>
 
 
 
* Se executamos esta aplicación podemos comprobar como cando se inicia o valor da variable vale 0.
 
: Modificaremos o contido da caixa de texto e premeremos no botón.
 
 
: Nese intre a nivel de programación a variable vale 5.
 
 
[[Imagen:PDM_Activity_20.jpg|500px|center]]
 
 
 
: Se marchamos da aplicación e volvemos, podemos comprobar como o contido da caixa de texto permanece e se prememos sobre o botón 'Ver Valor' tamén gardou o dato.
 
: Neste caso non teríamos que gardar o estado de nada para que a aplicación continuase (noutro caso, por exemplo, nun reprodutor de música poderíamos gardar a posición da canción que está sendo reproducida).
 
 
: Pero que pasa se o S.O. necesita memoria e elimina dita aplicación do Stack ?
 
: Tamén pasa o mesmo cando rotamos o dispositivos. A activity se 'recrea' novamente.
 
 
[[Imagen:PDM_Activity_21.jpg|500px|center]]
 
 
: Os datos perdéronse.
 
 
* Imos modificar o código para que se garden os datos introducidos:
 
<syntaxhighlight lang="java" line highlight="17-33">
 
package es.cursoandroid.cifprodolfoucha.aprendiendo.gardandoestado;
 
 
import android.app.Activity;
 
import android.os.Bundle;
 
import android.view.View;
 
import android.widget.Button;
 
import android.widget.Toast;
 
 
import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
 
 
 
public class UD2_01_gardando_estado extends Activity {
 
 
    private int valor = 0;
 
 
    @Override
 
    public void onSaveInstanceState(Bundle datosgardados){
 
 
        datosgardados.putInt("VALOR",valor);
 
 
        // SEMPRE O CODIGO POSTO POR NOS ANTES DA CHAMADA A SUPER.
 
        super.onSaveInstanceState(datosgardados);
 
    }
 
 
    @Override
 
    public void onRestoreInstanceState(Bundle datosgardados){
 
        super.onRestoreInstanceState(datosgardados);
 
 
        // CODIGO POSTO POR NOS PARA RECUPERAR OS DATOS, SEMPRE DESPOIS
 
        // DA CHAMADA A SUPER
 
        valor  = datosgardados.getInt("VALOR");
 
 
    }
 
 
 
    @Override
 
    protected void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
 
        setContentView(R.layout.activity_ud2_01_gardando_estado);
 
 
        Button btnBoton1 = findViewById(R.id.button1);
 
        btnBoton1.setOnClickListener(new View.OnClickListener() {
 
            @Override
 
            public void onClick(View v) {
 
                valor = 5;
 
                Toast.makeText(getApplicationContext(), "Valor: " + valor, Toast.LENGTH_LONG).show();
 
            }
 
        });
 
 
        Button btnBoton2 = findViewById(R.id.button2);
 
        btnBoton2.setOnClickListener(new View.OnClickListener() {
 
            @Override
 
            public void onClick(View v) {
 
                Toast.makeText(getApplicationContext(), "Valor: " + String.valueOf(valor), Toast.LENGTH_LONG).show();
 
            }
 
        });
 
 
 
    }
 
}
 
</syntaxhighlight>
 
 
 
 
 
 
* Podemos comprobar como agora ao rotar a pantalla os datos permanecen.
 
[[Imagen:PDM_Activity_22.jpg|500px|center]]
 
 
 
 
 
* Neste caso o view é gardado polo propio android, xa que todos os <u>view´s cun id posto </u> gardan o seu estado.
 
: De todas formas pode ser necesario gardar o estado dalgún view, xa que pode ser inicializado no método onStart.
 
: Por exemplo, cargar unha ListBox cun conxunto de datos da base de datos, que o usuario elixa un deses datos.
 
: Se nese momento a actividade se recrea, ao iniciarse, se volverían a cargar os datos da ListBox, pero a elección do usuario se perdería.
 
 
 
 
 
<br />
 
===Caso práctico 2===
 
*Neste caso imos modificar a aplicación primeira para implementar os métodos anteriores.
 
*Imos deshabilitar que o sistema garde, de modo automático, o valor do EditText ao xirar a pantalla e imos codificar o necesario para que ao xirar a pantalla se siga conservando o valor do EditText.
 
 
====O XML do Layout====
 
 
*Antes de ver o código Java, observar a '''Liña 22''' ('''android:saveEnabled="false"''') que lle indica ao sistema que non garde de modo automático o seu estado en caso de que, o sistema, teña que destruír a actividade.
 
*Realizar ese cambio no Layout da actividade e executar a actividade, introducir un valor no EditText e xirar o dispositivo. O EditTExt debera perder o seu valor.
 
 
<syntaxhighlight lang="xml" line enclose="div" highlight="18,22" >
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 
    android:layout_width="match_parent"
 
    android:layout_height="match_parent"
 
    android:orientation="vertical" >
 
 
    <LinearLayout
 
        android:layout_width="match_parent"
 
        android:layout_height="wrap_content"
 
        android:orientation="horizontal" >
 
 
        <Button
 
            android:layout_width="wrap_content"
 
            android:layout_height="wrap_content"
 
            android:onClick="onFinalizarClick"
 
            android:text="Finalizar" />
 
 
        <EditText
 
            android:id="@+id/et"
 
            android:layout_width="wrap_content"
 
            android:layout_height="wrap_content"
 
            android:hint="Texto"
 
            android:saveEnabled="false" />
 
    </LinearLayout>
 
 
    <ScrollView
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content" >
 
 
        <TextView
 
            android:layout_width="match_parent"
 
            android:layout_height="wrap_content"
 
            android:text="@string/instrucions" />
 
    </ScrollView>
 
 
</LinearLayout>
 
</syntaxhighlight>
 
 
====O código Java====
 
*Agora imos resolver o problema anterior:
 
 
<syntaxhighlight lang="java" line enclose="div" highlight="7,19-23,27-32,36-40" >
 
package com.example.u3_01_lifecycle;
 
 
import android.app.Activity;
 
import android.os.Bundle;
 
import android.view.Menu;
 
import android.view.View;
 
import android.widget.EditText;
 
import android.widget.Toast;
 
 
public class U3_01_LifeCycle extends Activity {
 
 
Bundle dato = new Bundle();
 
 
public void onCreate(Bundle savedInstanceState) {
 
super.onCreate(savedInstanceState);
 
setContentView(R.layout.activity_u3_01__life_cycle);
 
Toast.makeText(this, "Execútase: onCreate. Aproveitar para recuperar info da última sesión", Toast.LENGTH_SHORT).show();
 
 
/*if (savedInstanceState != null) {
 
EditText et = (EditText) findViewById(R.id.et);
 
et.setText(savedInstanceState.getString("VALOR_EDIT_TEXT"));
 
Toast.makeText(this, "Recreando", Toast.LENGTH_SHORT).show();
 
}*/
 
}
 
 
@Override
 
protected void onSaveInstanceState(Bundle estado) {
 
EditText et = (EditText) findViewById(R.id.et);
 
estado.putString("VALOR_EDIT_TEXT", et.getText().toString());
 
super.onSaveInstanceState(estado);
 
 
Toast.makeText(this, "Gardado estado: "+et.getText(), Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onRestoreInstanceState(Bundle savedInstanceState) {
 
super.onRestoreInstanceState(savedInstanceState);
 
EditText et = (EditText) findViewById(R.id.et);
 
et.setText(savedInstanceState.getString("VALOR_EDIT_TEXT"));
 
Toast.makeText(this, "Recreando", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onStart() {
 
super.onStart();
 
Toast.makeText(this, "Execútase: onStart", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onResume() {
 
super.onResume();
 
Toast.makeText(this, "Execútase: onResume", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onPause() {
 
super.onPause();
 
Toast.makeText(this, "Execútase: onPause. Aproveitar para gardar información por se se destrúe", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onRestart() {
 
super.onRestart();
 
Toast.makeText(this, "Execútase: onRestart", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onStop() {
 
super.onStop();
 
Toast.makeText(this, "Execútase: onStop", Toast.LENGTH_SHORT).show();
 
}
 
 
@Override
 
protected void onDestroy() {
 
super.onDestroy();
 
Toast.makeText(this, "Execútase: onDestroy. Saímos", Toast.LENGTH_SHORT).show();
 
}
 
 
public void onFinalizarClick(View v) {
 
finish();
 
}
 
 
 
@Override
 
public boolean onCreateOptionsMenu(Menu menu) {
 
// Inflate the menu; this adds items to the action bar if it is present.
 
getMenuInflater().inflate(R.menu.u3_01__life_cycle, menu);
 
return true;
 
}
 
 
}
 
 
</syntaxhighlight>
 
*'''Liñas 27-32''':
 
**Se xiramos a pantalla ou se o sistema ten que destruír a actividade vaise chamar ao método: '''onSaveInstanceState(Bundle)'''
 
**Neste caso sobrescribimos o método da clase View. Neste caso ao obxecto Bundle (estado) ímoslle engadindo parellas de '''CHAVE-VALOR''' ('''Liña 29'''):'''estado.putString("NOME_CONSTANTE","Texto que asignamos a NOME_CONSTANTE")'''
 
**Ao obxecto Bundle podemos engadirlle os pares que desexemos e do tipo de datos que precisemos. Por exemplo para enteiros: '''estado.putInt("CANCION",n_cancion)'''.
 
**Finalmente chamamos ao método onSaveInstanceState() da superclase, para que garde o estado do resto dos compoñentes da actividade. Ollo que non se chama ao principio!!!, pois ao final pasámoslle o obxecto '''estado''', cos pares CHAVE-VALOR.
 
 
 
*'''Liñas 36-40''':
 
**Ao '''Recrear''' a Actividade lanzase automaticamente '''onRestoreInstanceState(Bundle)'''.
 
**Neste caso sobrescribimos o método da superclase.
 
**Comezamos chamando ao método do pai para que estableza o estado dos elementos da actividade.
 
**Na '''liña 39''' recupérase o valor dun dos pares almacenado no Bundle.
 
 
 
*'''Liñas 19-23''':
 
**No canto de sobrescribir o método '''onRestoreInstanceState(Bundle)''' podemos recuperar os datos do Bundle no método onCreate()
 
**Pero o método onCreate() execútase cando se lanza a aplicación dende cero ou cando se "Recrea". No primeiro caso o Bundle vale null.
 
**Por iso temos que controlar se o obxecto Bundle ten valores (liña 19).
 
 
 
 
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] e [[Usuario:Carrion|Carlos Carrión Álvarez]] -- (2015).
 

Revisión actual del 19:47 13 nov 2020