Source: https://cursokotlin.com/capitulo-16-persistencia-datos-shared-preferences/
Timestamp: 2020-01-28 23:27:44+00:00

Document:
Capítulo 16 - Persistencia de datos con Shared Preferences - Curso Kotlin Para android
Portada » Capítulo 16 – Persistencia de datos con Shared Preferences
Públicado en 1 octubre, 2017 17 abril, 2019 por Aris
Un requisito básico para la mayoría de aplicaciones móviles a nivel profesional suele ser la persistencia de datos, con ella conseguimos no solo persistir información del usuario, sino que podemos almacenar contenido que ayuden a un funcionamiento mas óptimo, por ejemplo cacheando contenido que recuperamos de internet para no repetir las consultas cada vez que pasamos por dicha activity.
Las tres formas de almacenar información son a través de las Shared Preferences, ficheros o bases de datos. Como norma general no se suelen usar ficheros así que los descartaremos por ahora y en este capítulo veremos las Shared Preferences.
¿Que son las Shared Preferences?
Las Shared están destinadas a almacenar pequeñas cantidades de información a través de clave-valor. Debe ser información no comprometida puesto que Android generará un fichero XML donde almacenará toda esta información sin cifrar. Estos ficheros pueden ser compartidos o privados.
En este proyecto vamos a crear una aplicación muy sencilla en la cual si no hay ningún nombre guardado nos pedirá que lo pongamos, si por el contrario ya disponemos de uno nos saldrá en pantalla junto a un botón de borrar. Otra parte importante es que haremos nuestro primer patrón de diseño, el Singleton.
Lo primero que haremos será crear un nuevo proyecto y haremos una maquetación muy simple del layout.
<?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="com.cursokotlin.sharedpreferences.MainActivity"> <TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/etName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Escribe tu nombre" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/btnDeleteValue" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Borrar" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> <Button android:id="@+id/btnSaveValue" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Guardar" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> </android.support.constraint.ConstraintLayout>
tools:context="com.cursokotlin.sharedpreferences.MainActivity">
android:hint="Escribe tu nombre"
android:id="@+id/btnDeleteValue"
android:id="@+id/btnSaveValue"
Veréis que hay componentes que se sobreponen unos sobre otros, esto es porque así de paso os voy a enseñar los posibles casos que puede tener una vista.
Lo siguiente que haremos será crear una clase donde definiremos todo lo necesario para trabajar con shared, la llamaremos Prefs. Esta clase recibirá un contexto, en este caso el de la aplicación, para poder instanciarlo una sola vez al iniciar la aplicación y tener un objeto pref.
class Prefs (context: Context) { val PREFS_NAME = "com.cursokotlin.sharedpreferences" val SHARED_NAME = "shared_name" val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, 0) var name: String get() = prefs.getString(SHARED_NAME, "") set(value) = prefs.edit().putString(SHARED_NAME, value).apply() }
val PREFS_NAME = "com.cursokotlin.sharedpreferences"
val SHARED_NAME = "shared_name"
val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, 0)
get() = prefs.getString(SHARED_NAME, "")
set(value) = prefs.edit().putString(SHARED_NAME, value).apply()
Lo primero que hemos hecho ha sido definir dos constantes, PREFS_NAME y SHARED_NAME la primera será la clave del objeto pref que crearemos más adelante y la segunda la clave del nombre que almacenaremos. Recordad que las shared preferences se almacenan con clave-valor, lo que significa que para pedir el valor de «name» necesitamos pedirlo a través de la clave SHARED_NAME.
Observad también que hemos definido una variable name que será donde almacenemos el nombre como dije antes, pero que hemos sobreescrito el método get y set, así que cuando pidamos el valor de name, este accederá a el objeto prefs y pedirá dicho valor que corresponde la clave SHARED_NAME. Lo mismo con el set, que a través de prefs.edit().putString(SHARED_NAME, value).apply() almacenará el valor que le digamos. Obviamente si fuera otro tipo de variable, por ejemplo un Int, cambiaríamos el putString() por un putInt() y así con cada tipo de variable.
Ahora vamos a crear una clase algo diferente. Esta clase va a extender de Application() y eso significa que será lo primero en ejecutarse al abrirse la aplicación.
Para extender de dicha clase en Kotlin es muy sencillo, simplemente debemos crear una clase como siempre y a continuación del nombre después de dos puntos, pondremos la clase en cuestión.
class SharedApp : Application() { companion object { lateinit var prefs: Prefs } override fun onCreate() { super.onCreate() prefs = Prefs(applicationContext) } }
class SharedApp : Application() {
lateinit var prefs: Prefs
prefs = Prefs(applicationContext)
Aunque la clase sea bastante pequeña vamos a comentar unas cosas. Para empezar hemos declarado un companion object que será el objeto de nuestra clase Prefs que usaremos en todo el proyecto, así que para que lo entendáis el companion object es una forma de tener un objeto disponible en todo el proyecto (un objeto estático para los que conozcan java). Y delante lleva un lateinit que quiere decir que será instanciado más tarde, en este ejemplo en el método onCreate() de esta clase.
Recordad que para que esta clase se lance al abrir la app debemos ir al AndroidManifest.xml y añadir android:name=”.SharedApp ” dentro de la etiqueta <Application>
Ahora iremos a nuestro MainActivity a desarrollar un poco de lógica que falta.
La idea es crear dos métodos, uno mostrará una vista para invitados y el otro la vista del perfil, la diferencia entre ellos será que si eres invitada te mostrará un EditText y un botón de guardar y por el contrario si ya hay un nombre guardado en persistencia de datos pues te saludará y tendrá un botón para borrar dicho campo de memoria.
fun showProfile(){ tvName.visibility = View.VISIBLE tvName.text = "Hola ${SharedApp.prefs.name}" btnDeleteValue.visibility = View.VISIBLE etName.visibility = View.INVISIBLE btnSaveValue.visibility = View.INVISIBLE } fun showGuest(){ tvName.visibility = View.INVISIBLE btnDeleteValue.visibility = View.INVISIBLE etName.visibility = View.VISIBLE btnSaveValue.visibility = View.VISIBLE }
fun showProfile(){
tvName.visibility = View.VISIBLE
tvName.text = "Hola ${SharedApp.prefs.name}"
btnDeleteValue.visibility = View.VISIBLE
etName.visibility = View.INVISIBLE
btnSaveValue.visibility = View.INVISIBLE
fun showGuest(){
tvName.visibility = View.INVISIBLE
btnDeleteValue.visibility = View.INVISIBLE
etName.visibility = View.VISIBLE
btnSaveValue.visibility = View.VISIBLE
Como ya había dicho al comienzo del post, os quiero hablar sobre la visibilidad. Un componente puede estar en tres estados visible, invisible y gone.
Visible: El componente se ve en la pantalla, por defecto viene esta opción activada.
Invisible: El componente no se ve pero sigue estando en la pantalla, por lo que se puede seguir trabajando con él, por ejemplo poner algo a la derecha de un componente invisible.
Gone: El componente NO está en la pantalla por lo que no hay interacción posible.
Fijaros que el método showProfile asigna un valor a tvName, lo que significa que está accediendo a las shared preferences. Para ello simplemente llamamos a la clase SharedApp (la que contiene el companion object), el objeto del cual estamos hablando y el atributo name que como vimos al principio hemos modificado para que cuando hagamos un get (sacar la información que almacena) le pida nuestras shared preference el valor de SHARED_NAME.
Ahora necesitamos un método que compruebe si hay información en name y así comprobar si el usuario ha guardado su nombre.
fun configView(){ if(isSavedName()) showProfile() else showGuest() } fun isSavedName():Boolean{ val myName = SharedApp.prefs.name return myName != EMPTY_VALUE }
fun configView(){
if(isSavedName()) showProfile() else showGuest()
fun isSavedName():Boolean{
val myName = SharedApp.prefs.name
return myName != EMPTY_VALUE
Para finalizar solo debemos guardar o borrar la información del usuario cada vez que pulsemos el botón correspondiente y acto seguido volver a llamar a configView() para que muestre la vista oportuna.
btnSaveValue.setOnClickListener { SharedApp.prefs.name = etName.text.toString() configView() } btnDeleteValue.setOnClickListener { SharedApp.prefs.name = EMPTY_VALUE configView()}
btnSaveValue.setOnClickListener {
SharedApp.prefs.name = etName.text.toString()
configView() }
btnDeleteValue.setOnClickListener {
SharedApp.prefs.name = EMPTY_VALUE
configView()}
La clase completa quedaría así.
class MainActivity : AppCompatActivity() { val EMPTY_VALUE = "" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) configView() btnSaveValue.setOnClickListener { SharedApp.prefs.name = etName.text.toString() configView() } btnDeleteValue.setOnClickListener { SharedApp.prefs.name = EMPTY_VALUE configView()} } fun showProfile(){ tvName.visibility = View.VISIBLE tvName.text = "Hola ${SharedApp.prefs.name}" btnDeleteValue.visibility = View.VISIBLE etName.visibility = View.INVISIBLE btnSaveValue.visibility = View.INVISIBLE } fun showGuest(){ tvName.visibility = View.INVISIBLE btnDeleteValue.visibility = View.INVISIBLE etName.visibility = View.VISIBLE btnSaveValue.visibility = View.VISIBLE } fun configView(){ if(isSavedName()) showProfile() else showGuest() } fun isSavedName():Boolean{ val myName = SharedApp.prefs.name return myName != EMPTY_VALUE } }
val EMPTY_VALUE = ""
Con esto ya tendríamos lista nuestra primera app con persistencia de datos. Este ha sido un ejemplo muy básico pero prefiero ir poco a poco con este tema pues puede ser muy abundante si no vamos poco a poco.
22 comentarios sobre “Capítulo 16 – Persistencia de datos con Shared Preferences”
Buenas, os recuerdo que las novedades se irán publicando en la página de Facebook de la web. https://www.facebook.com/DevKotlin/
Hilario 29 octubre, 2017 Contestar
Simplemente genial, los artículos son muy concisos, explicas lo justo y necesario para entenderlos sin que sean pesados. Estoy deseando de que continues!!.
Por favor no lo dejes y muchas gracias por tu dedicación.
Aris Autor del artículo 29 octubre, 2017 Contestar
Estoy terminando un nuevo artículo ^^
Pingback: Capítulo 17 - Persistencia de datos con Room - Curso Kotlin Para android
Ivan Rodriguez 16 diciembre, 2017 Contestar
Holaaaa, soy ivan (si de nuevo :V) ¿En que momento se instancia la clase Prefs ? 🙁 mi app se crashea al lanzarla
Aris Autor del artículo 17 diciembre, 2017 Contestar
Buenas Iván, la clase Prefs se instancia en la clase ShareApp que extiende de application.
Me he fijado bien y faltaba una cosilla que he añadido.
Recordad que para que esta clase se lance al abrir la app debemos ir al AndroidManifest.xml y añadir android:name=”.SharedApp ” dentro de la etiqueta
Manauri 6 marzo, 2018 Contestar
hola soy nuevo me gustaria saber si puedo tener uno o mas valores almacenados con los shared preference saludos desde mexico muy buen tuto
Aris Autor del artículo 6 marzo, 2018 Contestar
Buenas, puedes almacenar todos los valores que quieras, aunque si van a ser muchos es recomendable usar una base de datos 🙂
Belisario Espinosa. 9 marzo, 2018 Contestar
Hola, soy nuevo y me encontré tus artículos, que son muy buenos por cierto, iba bien hasta este momento me tope con un error,
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property prefs has not been initialized
Aris Autor del artículo 12 marzo, 2018 Contestar
Buenas, eso pasa porque la variable prefs no ha sido inicializada. Se hace en el onCreate() de la clase SharedApp que extiende de application.
«Recordad que para que esta clase se lance al abrir la app debemos ir al AndroidManifest.xml y añadir android:name=”.SharedApp ” dentro de la etiqueta «
Belisario Espinosa. 12 marzo, 2018 Contestar
Lo hice pero por alguna extraña razón no funcionaba,
al parecer la clase SharedApp no extendía a Aplication(), la solución fue agregar esa misma linea al MainActivity quedando así:
SharedApp.prefs = Prefs(applicationContext)
…//}
fue lo mismo con la aplicación del artículo 17, ante todo te agradezco hermano.
daniel melo 16 octubre, 2019 Contestar
Al parecer no funciona si lo quieres llamar desde otra actividad me aparece el siguiente mensaje
Aris Autor del artículo 17 octubre, 2019 Contestar
Buenas, si puedes llamarlo de otra actividad. Tiene que haber algún problema de código en esta segunda actividad porque esta espera un String y le llega un Boolean. ¿Podrías añadir el código de la segunda activity a ver si encontramos el error 🙂 ?
Esteban Valencia 21 octubre, 2019 Contestar
Tengo un problema al lanza la app, ¿me podrías ayudar?
Así tengo mi manifest
Alberto 21 octubre, 2019 Contestar
Puedes darme un ejemplo de como queda el manifest?
No me corre la app
Que error te pone? Un saludo
Martruchagr 19 enero, 2020 Contestar
Lo primero de todo muchas gracias por el esfuerzo que realizas creando este curso.
Tengo un problema con este tema. La app que he creado (CursoKotlin2) siguiendo tus instrucciones, aparentemente está correcta, se genera y se ejecuta, pero no consigo que muestre la pantalla para solicitar/mostrar el nombre y los botones.
Aparece la pantalla pero en blanco. He hecho un debug y veo que se ejecuta la clase SharedApp, pero nunca pasa por el onCreate de MainActivity y lo que me muestra es una pantalla en blanco.
Cambie la activity y la formatee colocando los campos en su sitio y no dejándolos superpuestos unos con otros como se quedaban en tu ejemplo, pero nada.
No sé si me falta algo en el Manifest o en la clase SharedApp que haga que ejecute la MainActivity
Aris Autor del artículo 20 enero, 2020 Contestar
Puedes mostrarme el código de las 2 activities por favor? 🙂
Martruchagr 26 enero, 2020 Contestar
Parece que la pantalla no se ha pegado

References: artículo 29
 artículo 17
 artículo 6
 artículo 12
 artículo 17
 artículo 17
 artículo 20