Ciclo de vida dunha aplicación

De MediaWiki
Ir a la navegación Ir a la búsqueda

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:

00 basic-lifecycle.png.


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.

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

Se non o temos creado antes, crea un paquete de nome CicloVida como un subpaquete do teu paquete principal.


  • 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 xa vimos na creación do proxecto base.



  • 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.

PDM Activity 28.jpg


  • Escribimos algo na caixa de texto.

PDM Activity 29.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

PDM Activity 30.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.

PDM Activity 31.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:

00 basic-lifecycle-stopped.png

Ficheiros XML: Layout e strings

  • O Ficheiro /res/values/strings_ud03_01_ciclovida.xml
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3 
 4     <string name="str_ud03_01_instrucions">Ciclo de vida:\n\n
 5 		1. Ao iniciar á actividade lánzanse os métodos: onCreate, onStart e
 6 		onResume.\n\n
 7 
 8 		2. Se fas click no botón Atrás lánzanse os métodos
 9 		: onPause, onStop e onDestroy.\n\n
10 
11 		3. Cando se preme Finalizar os métodos: onPause, onStop e
12 		onDestroy.\n\n
13 
14 		4. A facer click Home lánzanse os métodos: onPause e onStop.\n\n
15 
16 		5. Se volvemos lanzar a actividade en espera lánzanse os métodos:
17 		onRestart, onStart e onResume)\n\n
18 
19 		6.- Se xiras a pantalla o valor do EditText Texto conservarase
20 		se este ten asignado un ID ou se garda o seu estado.
21     </string>
22 
23 </resources>



 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <LinearLayout
 8         android:layout_width="match_parent"
 9         android:layout_height="wrap_content"
10         android:orientation="horizontal" >
11 
12         <Button
13             android:id="@+id/btnFinalizar_UD03_01_CicloVida"
14             android:layout_width="wrap_content"
15             android:layout_height="wrap_content"
16             android:text="Finalizar" />
17 
18         <EditText
19             android:id="@+id/etDato_UD03_01_CicloVida"
20             android:layout_width="wrap_content"
21             android:layout_height="wrap_content"
22             android:hint="Texto" />
23     </LinearLayout>
24 
25     <ScrollView
26         android:layout_width="wrap_content"
27         android:layout_height="wrap_content" >
28 
29         <TextView
30             android:layout_width="match_parent"
31             android:layout_height="wrap_content"
32             android:text="@string/str_ud03_01_instrucions" />
33     </ScrollView>
34 
35 </LinearLayout>
  • Liña 13: 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 código da Activity

 1 package es.cursoandroid.cifprodolfoucha.aprendiendo.CicloVida;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.view.View;
 6 import android.widget.Button;
 7 import android.widget.Toast;
 8 
 9 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
10 
11 public class UD03_01_CicloVida extends Activity {
12 
13     private void xestionarEventos(){
14 
15         Button btnFinalizar = findViewById(R.id.btnFinalizar_UD03_01_CicloVida);
16         btnFinalizar.setOnClickListener(new View.OnClickListener() {
17             @Override
18             public void onClick(View v) {
19                 finish();
20             }
21         });
22 
23     }
24 
25     @Override
26     protected void onStart() {
27         super.onStart();
28         Toast.makeText(this, "Execútase: onStart", Toast.LENGTH_SHORT).show();
29     }
30 
31     @Override
32     protected void onResume() {
33         super.onResume();
34         Toast.makeText(this, "Execútase: onResume", Toast.LENGTH_SHORT).show();
35     }
36 
37     @Override
38     protected void onPause() {
39         super.onPause();
40         Toast.makeText(this, "Execútase: onPause. Aproveitar para gardar información por se se destrúe", Toast.LENGTH_SHORT).show();
41     }
42 
43     @Override
44     protected void onRestart() {
45         super.onRestart();
46         Toast.makeText(this, "Execútase: onRestart", Toast.LENGTH_SHORT).show();
47     }
48 
49     @Override
50     protected void onStop() {
51         super.onStop();
52         Toast.makeText(this, "Execútase: onStop", Toast.LENGTH_SHORT).show();
53     }
54 
55     @Override
56     protected void onDestroy() {
57         super.onDestroy();
58         Toast.makeText(this, "Execútase: onDestroy. Saímos", Toast.LENGTH_SHORT).show();
59     }
60 
61     @Override
62     protected void onCreate(Bundle savedInstanceState) {
63         super.onCreate(savedInstanceState);
64         setContentView(R.layout.activity_ud03_01__ciclo_vida);
65 
66         Toast.makeText(this, "Execútase: onCreate. Aproveitar para recuperar info da última sesión", Toast.LENGTH_SHORT).show();
67         xestionarEventos();
68     }
69 }
  • Os métodos callback están sobreescritos e chaman antes de nada aos métodos do pai.
  • Liñas 62,63: 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 19: 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.



  • 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:

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.

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.
PDM Activity 16.jpg


A este método lle pasa o obxecto da 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.
Nota: Antes de chegar o estado Resumed pasa polo estado Started.


  • Exemplo:
1 	@Override
2 	public void onSaveInstanceState(Bundle datosgardados){
3 		
4 		datosgardados.putInt(POSICION,reproductor.getPosicionActualCancion());
5 		datosgardados.putString(CANCION,reproductor.cancionactual);
6 
7 		// SEMPRE O CODIGO POSTO POR NOS ANTES DA CHAMADA A SUPER.
8 		super.onSaveInstanceState(datosgardados);
9 }


Para restaurar o sistema usaremos o método onRestoreInstanceState.
 1 	@Override
 2 	public void onRestoreInstanceState(Bundle datosgardados){
 3 		
 4 			super.onRestoreInstanceState(datosgardados);
 5 
 6 			// CODIGO POSTO POR NOS PARA RECUPERAR OS DATOS, SEMPRE DESPOIS 
 7 			// DA CHAMADA A SUPER	
 8 			reproductor.cancionactual=datosgardados.getString(C_DATOCANCION_PECHAR);
 9 			reproductor.posicioncancion=datosgardados.getInt(C_DATOPOSCANCION_PECHAR);
10 	
11 }
Nota: É importante chamar os métodos super.XXXXX na orde indicada no exemplo anterior.


Nota: Podemos facer que Android non garde o estado dun view poñendo como atributo: android:saveEnabled="false" no arquivo XML do layout ou por programación.





Caso Práctico 1

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.



  • 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 xa vimos na creación do proxecto base.


Cambia o contido do arquivo do Layout XML por este:
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     tools:context=".gardandoestado.UD2_01_gardando_estado">
 8 
 9 
10     <EditText
11         android:id="@+id/editText"
12         android:layout_width="wrap_content"
13         android:layout_height="wrap_content"
14         android:layout_marginBottom="8dp"
15         android:layout_marginEnd="8dp"
16         android:layout_marginStart="8dp"
17         android:layout_marginTop="8dp"
18         android:ems="10"
19         android:inputType="textPersonName"
20         android:text="Name"
21         app:layout_constraintBottom_toBottomOf="parent"
22         app:layout_constraintEnd_toEndOf="parent"
23         app:layout_constraintStart_toStartOf="parent"
24         app:layout_constraintTop_toTopOf="parent"
25         app:layout_constraintVertical_bias="0.073" />
26 
27     <Button
28         android:id="@+id/button1"
29         android:layout_width="wrap_content"
30         android:layout_height="wrap_content"
31         android:layout_marginEnd="8dp"
32         android:layout_marginStart="8dp"
33         android:layout_marginTop="84dp"
34         android:text="Dar Valor"
35         app:layout_constraintEnd_toEndOf="parent"
36         app:layout_constraintStart_toStartOf="parent"
37         app:layout_constraintTop_toBottomOf="@+id/editText" />
38 
39     <Button
40         android:id="@+id/button2"
41         android:layout_width="wrap_content"
42         android:layout_height="wrap_content"
43         android:layout_marginEnd="8dp"
44         android:layout_marginStart="8dp"
45         android:layout_marginTop="48dp"
46         android:text="Ver valor"
47         app:layout_constraintEnd_toEndOf="parent"
48         app:layout_constraintStart_toStartOf="parent"
49         app:layout_constraintTop_toBottomOf="@+id/button1" />
50 
51 </android.support.constraint.ConstraintLayout>


  • Cambio o contido da activity por este:
 1 package es.cursoandroid.cifprodolfoucha.aprendiendo.gardandoestado;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.view.View;
 6 import android.widget.Button;
 7 import android.widget.Toast;
 8 
 9 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
10 
11 
12 public class UD2_01_gardando_estado extends Activity {
13 
14     private int valor = 0;
15 
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_ud2_01_gardando_estado);
20 
21         Button btnBoton1 = findViewById(R.id.button1);
22         btnBoton1.setOnClickListener(new View.OnClickListener() {
23             @Override
24             public void onClick(View v) {
25                 valor = 5;
26                 Toast.makeText(getApplicationContext(), "Valor: " + valor, Toast.LENGTH_LONG).show();
27             }
28         });
29 
30         Button btnBoton2 = findViewById(R.id.button2);
31         btnBoton2.setOnClickListener(new View.OnClickListener() {
32             @Override
33             public void onClick(View v) {
34                 Toast.makeText(getApplicationContext(), "Valor: " + String.valueOf(valor), Toast.LENGTH_LONG).show();
35             }
36         });
37 
38 
39     }
40 }


  • 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.
PDM Activity 20.jpg


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.
PDM Activity 21.jpg
Os datos perdéronse.
  • Imos modificar o código para que se garden os datos introducidos:
 1 package es.cursoandroid.cifprodolfoucha.aprendiendo.gardandoestado;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.view.View;
 6 import android.widget.Button;
 7 import android.widget.Toast;
 8 
 9 import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
10 
11 
12 public class UD2_01_gardando_estado extends Activity {
13 
14     private int valor = 0;
15 
16     @Override
17     public void onSaveInstanceState(Bundle datosgardados){
18 
19         datosgardados.putInt("VALOR",valor);
20 
21         // SEMPRE O CODIGO POSTO POR NOS ANTES DA CHAMADA A SUPER.
22         super.onSaveInstanceState(datosgardados);
23     }
24 
25     @Override
26     public void onRestoreInstanceState(Bundle datosgardados){
27         super.onRestoreInstanceState(datosgardados);
28 
29         // CODIGO POSTO POR NOS PARA RECUPERAR OS DATOS, SEMPRE DESPOIS
30         // DA CHAMADA A SUPER
31         valor   = datosgardados.getInt("VALOR");
32 
33     }
34 
35 
36     @Override
37     protected void onCreate(Bundle savedInstanceState) {
38         super.onCreate(savedInstanceState);
39         setContentView(R.layout.activity_ud2_01_gardando_estado);
40 
41         Button btnBoton1 = findViewById(R.id.button1);
42         btnBoton1.setOnClickListener(new View.OnClickListener() {
43             @Override
44             public void onClick(View v) {
45                 valor = 5;
46                 Toast.makeText(getApplicationContext(), "Valor: " + valor, Toast.LENGTH_LONG).show();
47             }
48         });
49 
50         Button btnBoton2 = findViewById(R.id.button2);
51         btnBoton2.setOnClickListener(new View.OnClickListener() {
52             @Override
53             public void onClick(View v) {
54                 Toast.makeText(getApplicationContext(), "Valor: " + String.valueOf(valor), Toast.LENGTH_LONG).show();
55             }
56         });
57 
58 
59     }
60 }



  • Podemos comprobar como agora ao rotar a pantalla os datos permanecen.
PDM Activity 22.jpg



  • Neste caso o view é gardado polo propio android, xa que todos os view´s cun id posto 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.




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.
 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:layout_width="match_parent"
 3     android:layout_height="match_parent"
 4     android:orientation="vertical" >
 5 
 6     <LinearLayout
 7         android:layout_width="match_parent"
 8         android:layout_height="wrap_content"
 9         android:orientation="horizontal" >
10 
11         <Button
12             android:layout_width="wrap_content"
13             android:layout_height="wrap_content"
14             android:onClick="onFinalizarClick"
15             android:text="Finalizar" />
16 
17         <EditText
18             android:id="@+id/et"
19             android:layout_width="wrap_content"
20             android:layout_height="wrap_content"
21             android:hint="Texto"
22             android:saveEnabled="false" />
23     </LinearLayout>
24 
25     <ScrollView
26         android:layout_width="wrap_content"
27         android:layout_height="wrap_content" >
28 
29         <TextView
30             android:layout_width="match_parent"
31             android:layout_height="wrap_content"
32             android:text="@string/instrucions" />
33     </ScrollView>
34 
35 </LinearLayout>

O código Java

  • Agora imos resolver o problema anterior:
 1 package com.example.u3_01_lifecycle;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.view.Menu;
 6 import android.view.View;
 7 import android.widget.EditText;
 8 import android.widget.Toast;
 9 
10 public class U3_01_LifeCycle extends Activity {
11 
12 	Bundle dato = new Bundle();
13 
14 	public void onCreate(Bundle savedInstanceState) {
15 		super.onCreate(savedInstanceState);
16 		setContentView(R.layout.activity_u3_01__life_cycle);
17 		Toast.makeText(this, "Execútase: onCreate. Aproveitar para recuperar info da última sesión", Toast.LENGTH_SHORT).show();
18 		
19 		/*if (savedInstanceState != null) {
20 			EditText et = (EditText) findViewById(R.id.et);
21 			et.setText(savedInstanceState.getString("VALOR_EDIT_TEXT"));
22 			Toast.makeText(this, "Recreando", Toast.LENGTH_SHORT).show();
23 		}*/
24 	}
25 
26 	@Override
27 	protected void onSaveInstanceState(Bundle estado) {
28 		EditText et = (EditText) findViewById(R.id.et);
29 		estado.putString("VALOR_EDIT_TEXT", et.getText().toString());
30 		super.onSaveInstanceState(estado);
31 
32 		Toast.makeText(this, "Gardado estado: "+et.getText(), Toast.LENGTH_SHORT).show();
33 	}
34 
35 	@Override
36 	protected void onRestoreInstanceState(Bundle savedInstanceState) {
37 		super.onRestoreInstanceState(savedInstanceState);
38 		EditText et = (EditText) findViewById(R.id.et);
39 		et.setText(savedInstanceState.getString("VALOR_EDIT_TEXT"));
40 		Toast.makeText(this, "Recreando", Toast.LENGTH_SHORT).show();
41 	}
42 	
43 	@Override
44 	protected void onStart() {
45 		super.onStart();
46 		Toast.makeText(this, "Execútase: onStart", Toast.LENGTH_SHORT).show();
47 	}
48 
49 	@Override
50 	protected void onResume() {
51 		super.onResume();
52 		Toast.makeText(this, "Execútase: onResume", Toast.LENGTH_SHORT).show();
53 	}
54 
55 	@Override
56 	protected void onPause() {
57 		super.onPause();
58 		Toast.makeText(this, "Execútase: onPause. Aproveitar para gardar información por se se destrúe", Toast.LENGTH_SHORT).show();			
59 	}
60 
61 	@Override
62 	protected void onRestart() {
63 		super.onRestart();
64 		Toast.makeText(this, "Execútase: onRestart", Toast.LENGTH_SHORT).show();
65 	}
66 
67 	@Override
68 	protected void onStop() {
69 		super.onStop();
70 		Toast.makeText(this, "Execútase: onStop", Toast.LENGTH_SHORT).show();
71 	}
72 
73 	@Override
74 	protected void onDestroy() {
75 		super.onDestroy();
76 		Toast.makeText(this, "Execútase: onDestroy. Saímos", Toast.LENGTH_SHORT).show();
77 	}
78 
79 	public void onFinalizarClick(View v) {
80 		finish();
81 	}
82 
83 	
84 	@Override
85 	public boolean onCreateOptionsMenu(Menu menu) {
86 		// Inflate the menu; this adds items to the action bar if it is present.
87 		getMenuInflater().inflate(R.menu.u3_01__life_cycle, menu);
88 		return true;
89 	}
90 
91 }
  • 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).



-- Ángel D. Fernández González e Carlos Carrión Álvarez -- (2015).