sábado, 18 de julio de 2015

Curso de Android: Shared Preferences


Las preferencias son datos necesarios para el funcionamiento de nuestra aplicacion que almacenaremos en disco,  no son más que datos que una aplicación debe guardar para personalizar la experiencia del usuario, ejemplo autentificaciones realizadas que no tiene sentido realizarlas en cada acceso , configuraciones de básicas de una aplicación , etc.

Las preferencias de una aplicación se podrían almacenar utilizando sqlite, y no tendría nada de malo, pero Android proporciona otro método alternativo diseñado específicamente para administrar este tipo de datos: las preferencias compartidas o shared preferences.

Cada preferencia se almacenará en forma de clave-valor, es decir, cada una de ellas estará compuesta por un identificador único (p.e. “email”) y un valor asociado a dicho identificador (p.e. “prueba@email.com”). La información de la preferencia es resguardada en archivos XML

Toda la gestión se centraliza en la clase SharedPrefences, que representará a una colección de preferencias. Una aplicación Android puede gestionar varias colecciones de preferencias, que se diferenciarán mediante un identificador único.

Para obtener una referencia a una colección determinada utilizaremos el métodogetSharedPrefences() al que pasaremos el identificador de la colección y un modo de acceso.

El modo de acceso indicará qué aplicaciones tendrán acceso a la colección de preferencias y qué operaciones tendrán permitido realizar sobre ellas. Así, tendremos tres posibilidades principales:
  • MODE_PRIVATE. Sólo nuestra aplicación tiene acceso a estas preferencias.
  • MODE_WORLD_READABLE. Todas las aplicaciones pueden leer estas preferencias, pero sólo la nuestra puede modificarlas.
  • MODE_WORLD_WRITABLE. Todas las aplicaciones pueden leer y modificar estas preferencias.
Nota: las ultimas dos opciones fueran definidas como deprecadas a partir de la api 17 por ser peligrosas.
Para obtener una referencia a una colección de preferencias llamada por ejemplo “MisPreferencias” y como modo de acceso exclusivo para nuestra aplicación haríamos lo siguiente:

SharedPreferences prefs = getSharedPreferences("MisPreferencias",Context.MODE_PRIVATE);

Una vez hemos obtenido una referencia a nuestra colección de preferencias, ya podemos obtener, insertar o modificar preferencias utilizando los métodos get o put correspondientes al tipo de dato de cada preferencia. Así, por ejemplo, para obtener el valor de una preferencia llamada “email” de tipo String escribiríamos lo siguiente:

SharedPreferences prefs =getSharedPreferences("MisPreferencias",Context.MODE_PRIVATE);
String correo = prefs.getString("email", por_defecto@email.com);

Como vemos, al método getString() le pasamos el nombre de la preferencia que queremos recuperar y un segundo parámetro con un valor por defecto. Este valor por defecto será el devuelto por el método getString() si la preferencia solicitada no existe en la colección.

Además del método getString(), existen por supuesto métodos análogos para el resto de tipos de datos básicos, por ejemplogetInt(), getLong(), getFloat(), getBoolean(), …

Para actualizar o insertar nuevas preferencias el proceso será igual de sencillo, con la única diferencia de que la actualización o inserción no la haremos directamente sobre el objeto SharedPreferences, sino sobre su objeto de edición SharedPreferences.Editor. A este último objeto accedemos mediante el método edit() de la clase SharedPreferences.

SharedPreferences prefs =getSharedPreferences("MisPreferencias",Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("email", "modificado@email.com");
editor.putString("nombre", "Prueba");
editor.commit();

Una vez obtenida la referencia al editor, utilizaremos los métodos put correspondientes al tipo de datos de cada preferencia para actualizar/insertar su valor, por ejemploputString(clave, valor), para actualizar una preferencia de tipo String. De forma análoga a los métodos get que ya hemos visto, tendremos disponibles métodos put para todos los tipos de datos básicos: putInt(), putFloat(), putBoolean(), etc. Finalmente, una vez actualizados/insertados todos los datos necesarios llamaremos al método commit() para confirmar los cambios.

¿Pero donde se almacenan estas preferencias compartidas?

Como dijimos al comienzo del artículo, las preferencias no se almacenan en ficheros binarios como las bases de datos SQLite, sino en ficheros XML. Estos ficheros XML se almacenan en una ruta que sigue el siguiente patrón:

/data/data/paquete.java/shared_prefs/nombre_coleccion.xml
¿ Desde donde puede ver este archivo en el emulador ?

Desde el android device Monitor podremos verificar toda la actividad de nuestro monitor y podemos bajar a nuestra maquina.

image
Nuestro Ejemplo

Un pequeño ejemplo donde usamos preferencias para reguardar el login en una aplicación para que en nuestro proximo ingreso ya no solicite realizar nuevamente el login.
Tendremos una pantalla de login

image

si el usuario ingresa un dato incorrecto nos indica un mensaje

image

y tendremos un pantalla home en caso de ingresar usuario:admin clave:admin

image

Nota: el uso de preferencias de usuarios nos proporciona que al momento de realizar un login valido el poder resguardar esta información para que al próximo ingreso ya no solicite el login nuevamente. En nuestro próximo ingreso directamente ingresara a home.


activity_login.xml:  como podemos ver no presenta grandes complicaciones la interfaz, no es otra cosa que nuestra pantalla login antes mostrada.


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.edu.sharepreference001.LoginActivity">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:weightSum="1"
        android:gravity="center_horizontal">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="127dp"
            android:id="@+id/imageView"
            android:src="@mipmap/ic_launcher"
            android:layout_weight="0.23" />
    </LinearLayout>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Nombre de Usuario:"
        android:id="@+id/textView" />

    <EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editUsuario" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Clave Usuario:"
        android:id="@+id/textView2" />

    <EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:password="true"
        android:id="@+id/editClave" />

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Login"
            android:id="@+id/btnLogin" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Cancelar"
            android:id="@+id/btnCancelar" />
    </LinearLayout>

</LinearLayout>

LoginActivity.java : es nuestra logica de la aplicacion que es donde podremos encontrar el uso de sharedpreference y las validaciones necesarias para funcionamiento.


package com.edu.sharepreference001;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;


public class LoginActivity extends ActionBarActivity {

    Button btnLogin;
    Button btnCancelar;
    EditText editUsuario;
    EditText editClave;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final SharedPreferences mipref = getSharedPreferences("mipreferencia", Context.MODE_PRIVATE);
        if (mipref.getBoolean("login", false))
        {
            Intent intencion = new Intent(getApplicationContext(), homeActivity.class);
            startActivity(intencion);
            finish();
        }

        setContentView(R.layout.activity_login);

        editUsuario = (EditText)findViewById(R.id.editUsuario);
        editClave = (EditText)findViewById(R.id.editClave);

        btnLogin = (Button) findViewById(R.id.btnLogin);
        btnCancelar = (Button) findViewById(R.id.btnCancelar);

        btnCancelar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Log.i("logdiego", "paso por aqui usaurio " + editUsuario.getText().toString());
                Log.i("logdiego","paso por aqui clave " + editClave.getText().toString());


                if ( (editUsuario.getText().toString().equals("admin"))
                        && ( editClave.getText().toString().equals("admin") ) )
                {
                    Log.i("logdiego", "verdadero");

                    SharedPreferences.Editor editor = mipref.edit();
                    editor.putBoolean("login", true);
                    editor.commit();

                    Intent intencion = new Intent(getApplicationContext(), homeActivity.class);
                    startActivity(intencion);

                    finish();
                }
                else
                {

                    Toast.makeText(getApplicationContext(),"Usuario i/o clave invalidos",Toast.LENGTH_SHORT).show();
                    Log.i("logdiego", "falso");
                }

            }
        });


    }

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Pero analicemos un poco el codigo en la siguiente lineas obtenemos la preferencia de usuario llamada en "mipreferencia" en modo privado es decir que solo podra ser utilizada por la aplicacion propia. 

en la siguiente linea consultamos si la key  "login" es true , en caso de que se cumpla la condición generamos una intención y nos trasladamos de manera automática a la actividad home. Finalmente cerramos la actividad actual.

final SharedPreferences mipref = getSharedPreferences("mipreferencia", Context.MODE_PRIVATE);
        if (mipref.getBoolean("login", false))
        {
            Intent intencion = new Intent(getApplicationContext(), homeActivity.class);
            startActivity(intencion);
            finish();
        }

En el siguiente codigo solo realizamos la validacion de los datos ingresados por el usuario si el usuario y la clave es admin.  Si sucede este caso pondremos en modo de edicion la preferencia por medio de la clase editor y posterior con putBoolean asinamos a la key "login" el valor true y finalmente resguardamos la informacion con commit.

if ( (editUsuario.getText().toString().equals("admin"))
                        && ( editClave.getText().toString().equals("admin") ) )
                {
                    Log.i("logdiego", "verdadero");

                    SharedPreferences.Editor editor = mipref.edit();
                    editor.putBoolean("login", true);
                    editor.commit();

                    Intent intencion = new Intent(getApplicationContext(), homeActivity.class);
                    startActivity(intencion);

                    finish();
                }
                else
                {

                    Toast.makeText(getApplicationContext(),"Usuario i/o clave invalidos",Toast.LENGTH_SHORT).show();
                    Log.i("logdiego", "falso");
                }


El código del proyecto se puede descargar desde aqui.

https://dl.dropboxusercontent.com/u/91473640/Curso%20Android/SharePreference001.rar