Sei sulla pagina 1di 16

Persistencia de datos

La persistencia de los datos se refiere a los mecanismos utilizados para


que la información de una aplicación esté disponible a lo largo del
tiempo. En Android existen básicamente 3 mecanismos de persistencia:

● Shared preferences. Sirve para almacenar pequeñas porciones de


datos.
● Almacenamiento en sistema de archivos
● Almacenamiento en un DBMS (Database management system)
relacional (SQL Lite).

Shared Preferences

El objeto sharedPreferences nos permite almacenar información usando


pares de claves/nombres y sus valores. Se utiliza principalmente como
un método simple para almacenar datos relacionados a las propiedades
de la aplicación, los cuales se guardan en un xml.

Para utilizar el archivo de preferencias dentro de un proyecto, se siguen


los siguientes pasos:

1. Crear una carpeta llamada xml dentro del directorio de recursos


(res). Se genera un archivo xml.

2. Dentro del archivo se coloca la estructura del xml con los


componentes de las preferencias (por ejemplo PreferenceScreen,
PreferenceCategory, CheckboxPreference, EditTextPreference,
etc.)

<?xml version=”1.0” encoding=”utf-8”?>


<PreferenceScreen
xmlns:android=”http://schemas.android.com/apk/res/android”>
<PreferenceCategory android:title=”Category 1”>
<CheckBoxPreference
android:title=”Checkbox”
(.
</PreferenceCategory>
<PreferenceCategory android:title=”Category 2”>
<EditTextPreference
(.
android:key=”editTextPref” />
<RingtonePreference
.../>
<PreferenceScreen
android:title=”Second Preference Screen”
(
Los atributos tienen el formato android:[key] = “valor”

3. Se crea una actividad para las preferencias


(AppPreferenceActivity en el ejemplo) y se carga el archivo de
preferencias xml usando el método AddPreferencesFromResource

public class AppPreferenceActivity extends PreferenceActivity {


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.myapppreferences);
}

Nota: En versiones más actuales de Android se deben utilizar


PreferenceFragments

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getFragmentManager()
.beginTransaction()
.replace(android.R.id.content, new PrefereceFragment1())
.commit();
}
public static class PrefereceFragment1 extends
PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.myapppreferences);
}

4. Desde otra actividad se invoca a la actividad de preferencias y se


despliega una pantalla en donde podemos registrarlas. Es
necesario registrar el archivo de preferencias en el
AndroidManifest.

public void onClickLoad(View view)


{Intent i = new Intent(
"edu.uda.usingpreferences2.AppPreferenceActivity");
startActivity(i);
}

5. Si se registra algún valor para una preferencia, se crea un


archivo en la carpeta /data/data/[paquete].[aplicación]
/shared_prefs (Para confirmar podemos ir a la perspectiva
DDMS y buscar la ruta adecuada).

El xml generado tiene el siguiente formato (que podemos verificar


si descargamos el archivo):

<?xml version=’1.0’ encoding=’utf-8’ standalone=’yes’ ?>


<map>
<string name=”editTextPref”>[Enter a string here]</string>
<string name=”ringtonePref”></string>
</map>

Recuperar o modificar valores de preferencias


programáticamente.
Para recuperar el valor de una preferencia, en el método
correspondiente (onClickDisplay()) creamos una instancia de
SharedPreferences y en ella almacenamos la referencia al archivo de
preferencias que se recupera con el método getSharedPreferences, al
cual se le pasan dos parámetros:

● La referencia al paquete de preferencias


(edu.uda.usingpreferences2_preferences).

● El modo de apertura (en este caso MODE_PRIVATE). El modo


privado indica que el archivo de preferencias puede abrirse
sólo por la aplicación que lo creó.

public void onClickDisplay(View view)


{
SharedPreferences appPrefs getSharedPreferences(
"edu.uda.usingpreferences2_preferences",MODE_PRIVATE);
DisplayText(appPrefs.getString("editTextPref",
""));

Si deseamos modificar el valor de una propiedad seguimos los


siguientes pasos:

1. Obtenemos una referencia al archivo de preferencias


2. Creamos una instancia al objeto SharedPreferences.Editor para
actualizar la información.
3. Colocamos el valor del nuevo texto (método putString pasando
como parámetro la clave)
4. Realizamos un commit sobre el editor de las preferencias
para guardar los cambios.

public void onClickModify(View view)


{ SharedPreferences
appPrefs = getSharedPreferences ("archivodepreferencias",
MODE_PRIVATE);
SharedPreferences.Editor prefsEditor = appPrefs.edit();
prefsEditor.putString("editTextPref",
((EditText) findViewById(R.id.txtString)).getText().toString());
prefsEditor.commit();
}

Para cambiar el nombre del archivo de preferencias, obtenemos una


instancia del PreferenceManager y se modifica el nombre usando el
método setSharedPreferencesName().

PreferenceManager manager = getPreferenceManager();


manager.setSharedPreferencesName("archivodepreferencias");

Este cambio provocará que se tenga que invocar el archivo de


preferencias con el nuevo nombre

Grabar datos en archivos

En el caso de que se quiera grabar información en el sistema de


archivos tradicional, generalmente para almacenar información más
compleja que valores de propiedades, se utiliza el paquete java.io. Para
grabar información en un archivo se siguen los siguientes pasos:

1. Se debe definir una actividad, dentro de la cual se incluirá un


método de grabación (en el ejemplo, el método onClickSave)

public void onClickSave(View view) {


String str = textBox.getText().toString();
2. Definir una instancia del objeto FileOutputStream para poder
grabar el archivo. El método openFileOutput define el nombre del
archivo en donde se grabará la información y una constante que
indica si el archivo puede ser leído por otras aplicaciones
(MODE_WORLD_READABLE), si sólo puede ser abierto por la
aplicación que lo creó (MODE_PRIVATE), si va a añadir
información a un archivo existente (MODE_APPEND) o si otras
aplicaciones pueden tener acceso de escritura a él
(MODE_WORLD_WRITEABLE).

FileOutputStream fOut = openFileOutput(“textfile.txt”,


MODE_WORLD_READABLE);
3. Creamos una instancia de un stream de salida para convertir
el flujo de caracteres en un stream de bytes. Para ello pasamos
como parámetro el FileOutputStream al constructor del
OutputStreamWriter.

OutputStreamWriter osw = new OutputStreamWriter(fOut);

4. Para escribir una cadena al archivo, usamos el método write del


OutputStreamWriter
osw.write(str);
osw.flush();

5. Finalmente cerramos el OutputStreamWriter


osw.close();

Para recuperar la información almacenada en un archivo, se debe


realizar lo siguiente (en el método correspondiente):

1. Crear una instancia de FileInputStream y abrir el archivo de texto


requerido.

FileInputStream fIn = openFileInput(“textfile.txt”);

2. Creamos una instancia de InputStreamReader a fin de obtener


el stream de entrada. El constructor recibe como parámetro la
instancia de FileInputStream.
InputStreamReader isr = new InputStreamReader(fIn);

3. Como se desconoce el tamaño del archivo a leer, el contenido se


leerá en bloques de caracteres (en el ejemplo, 100) que se
almacenan en un arreglo de caracteres (buffer)

static final int READ_BLOCK_SIZE = 100;


char[] inputBuffer = new char[READ_BLOCK_SIZE];

4. Se lee el InputStreamReader con el método read mientras existan


valores
while ((charRead = isr.read(inputBuffer))>0)
{
String readString =
String.copyValueOf(inputBuffer, 0,charRead);

Para verificar la grabación del archivo se puede abrir la perspectiva


DDMS en /data/data/[paquete].Files/files

Grabar datos en almacenamiento externo (SD Card)

Para almacenar información en el almacenamiento externo (tarjeta SD)


se deben modificar los métodos de lectura y grabación.

En el proceso de grabación se siguen los siguientes pasos:

1. Se instancia un objeto File que almacenará el path completo de la


tarjeta sd, y cuya referencia se obtiene a través del método
Environment.getExternalStorageDirectory().

Es necesario usar este método en lugar de colocar rutas estáticas


directamente en código (“/sdcard/” en el caso de un dispositivo
real o “/mnt/” en el caso de un emulador) porque este valor puede
variar según la definición establecida por los fabricantes.

File sdCard = Environment.getExternalStorageDirectory();

2. Se obtiene la referencia a un directorio (con el path de la tarjeta


sd) y se crea

File directory = new File (sdCard.getAbsolutePath() + “/MyFiles”);


directory.mkdirs();

3. Creamos el archivo a procesar pasando como parámetro


directorio y el nombre del archivo

File file = new File(directory, “textfile.txt”);

4. Instanciamos un objeto FileOutputStream pasándole como


parámetro el archivo creado en el punto previo.

FileOutputStream fOut = new FileOutputStream(file);


Se siguen los pasos antes descritos

En el proceso de lectura los cambios son similares (el path del SD)

File sdCard = Environment.getExternalStorageDirectory();


File directory = new File (sdCard.getAbsolutePath() + “/MyFiles”);
File file = new File(directory, “textfile.txt”);
FileInputStream fIn = new FileInputStream(file);

Finalmente, para ambos procesos es necesario agregar el permiso


correspondiente en el archivo AndroidManifest.xml

<uses-permission
android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />

Se puede consultar el archivo creado en la ruta /mnt/sdcard/MyFiles/.

Si se desea trabajar con archivos precargados en la aplicación, se debe


crear la carpeta raw en el directorio “res” y colocar los archivos que se
desea incluir.

InputStream is =
this.getResources().openRawResource(R.raw.textfile);
BufferedReader br = new BufferedReader(new
InputStreamReader(is));
String str = null;
try {
...}
is.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}

Nota: Si el nombre del archivo es textfile.txt, su id de recurso es


R.raw.textfile.
Crear y usar bases de datos

Android utiliza típicamente la base de datos relacional SqlLite, la cual


nos proporciona facilidades como el control de integridad, eficiencia de
consulta, etc.

Las bases de datos creadas programáticamente se almacenan en la


ruta /data/data/<package_name>/databases.

Una buena práctica de programación en la gestión de bases de datos,


consiste en crear una clase auxiliar (helper) que encapsule las
operaciones realizadas con la base de datos, de forma que su
complejidad sea transparente al resto de procesos.

Para crear la clase helper y sus distintas operaciones, seguimos los


siguientes pasos:

1. Definimos la clase que gestionará la BD y los atributos a utilizar

public class DBAdapter {


static final String KEY_ROWID = “_id”;
static final String KEY_NAME = “name”;
static final String KEY_EMAIL = “email”;
static final String TAG = “DBAdapter”;
static final String DATABASE_NAME = “MyDB”;
static final String DATABASE_TABLE = “contacts”;
static final int DATABASE_VERSION = 1;

...
final Context context;
DatabaseHelper DBHelper;
SQLiteDatabase db;

2. Definimos la clase DatabaseHelper heredando de la clase


SQLiteOpenHelper. En el constructor se invoca al constructor de
SQLiteOpenHelper, el cual requiere el contexto, la base de datos,
la versión, etc.
En el método onCreate se pasa la instancia de la base de datos y
se ejecuta la cadena que contiene la sentencia que crea la base
de datos

private static class DatabaseHelper extends SQLiteOpenHelper


{DatabaseHelper(Context context)
{super(context, DATABASE_NAME, null,
DATABASE_VERSION); }

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
...

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int
newVersion)
{(
db.execSQL(“DROP TABLE IF EXISTS contacts”);
onCreate(db);

El método onUpgrade se ejecuta cada vez que se detecta una


nueva versión de la BD. Elimina la base anterior y la vuelve a
crear.

3. Se definen los métodos para abrir y cerrar la base de datos

public DBAdapter open() throws SQLException


{ db = DBHelper.getWritableDatabase();
return this;
}

public void close()


{ DBHelper.close();}
4. Se crean los métodos para ingresar, actualizar, eliminar y
consultar contactos (ver ejemplo)

public long insertContact(String name, String email)


{ContentValues initialValues = new ContentValues();
initialValues.put(KEY_NAME, name);
initialValues.put(KEY_EMAIL, email);
return db.insert(DATABASE_TABLE, null, initialValues);
}

ContentValues usa pares de valores.

public boolean deleteContact(long rowId)


{ return db.delete(DATABASE_TABLE, KEY_ROWID + “=” +
rowId, null) ...;
}

public boolean updateContact(long rowId, String name, String


email)
{
ContentValues args = new ContentValues();
args.put(KEY_NAME, name);
args.put(KEY_EMAIL, email);
return db.update(DATABASE_TABLE, args, KEY_ROWID + “=” +
rowId, null) > 0;
}

public Cursor getContact(long rowId) throws SQLException


{
Cursor mCursor =
db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
KEY_NAME, KEY_EMAIL}, KEY_ROWID + “=” + rowId, null,
null, null, null, null);

//query(tableName, tableColumns, whereClause, whereArgs,


groupBy, having, orderBy);

if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
5. Una vez que se ha definido la clase para gestionar la base de
datos, se prueban sus métodos.

// Ingresar
DBAdapter db = new DBAdapter(this);
db.open();
id = db.insertContact("Juan Perez", "juanperez@uazuay.edu.ec");
...
db.close();

// Borrar contacto
db.open();
if (db.deleteContact(4))
...
db.close();

// Actualizar contacto

db.open();
if (db.updateContact(3, "Ismael Merchan",
"imerchan@uazuay.edu.ec"))
...
db.close();

// Recuperar todos los contactos

db.open();
Cursor c = db.getAllContacts();
if (c.moveToFirst())
{ do { DisplayContact(c);}
while (c.moveToNext());
}
db.close();
// Recuperar un contacto

db.open();
Cursor c = db.getContact(2);
if (c.moveToFirst())
DisplayContact(c);
...
db.close();

Para actualizar la base de datos, se debe actualizar la constante


DATABASE_VERSION colocando un valor más alto que el anterior. La
operación borra la base previa y crea una nueva

Usar bases de datos pre-existentes


Normalmente las bases de datos no son creadas en runtime sino se
desarrollan en diseño y luego se importan para utilizarse por las
aplicaciones.

Se puede usar varias herramienta para crear una base de datos SQL
Lite y luego se importa en la aplicación.

En Ubuntu podemos usar el gestor sqliteman que está en los


repositorios del sistema. Para instalarlo seguimos los siguientes pasos:

1. Descargar sqliteman con los comandos

Con proxy
sudo http_proxy='http://username:XXX@proxy.internet.com:8002/'
apt-get update

sudo http_proxy='http://username:XXX@proxy.internet.com:8002/'
apt-get install sqliteman

Sin proxy

sudo apt-get update


sudo apt-get install sqliteman

2. Ejecutar como administrador


/usr/bin$ sudo ./sqliteman

Si no se conoce la ruta usar el comando: whereis sqliteman


3. Para crear la base de datos seleccionamos File – New –
Database. Colocamos la ruta de creación y el nombre

4. Para crear la tabla de contactos nos ponemos sobre Tables,


damos click derecho y ponemos Create Table

5. En la ventana vamos ingresando los campos (es equivalente a


ejecutar el siguiente comando: CREATE TABLE contacts(id
integer primary key autoincrement, name text not null, email text
not null))

6. Ingresamos datos en las tablas.


insert into contacts ("id", "name", "email") values ('1', 'Juan Perez',
'juanperez@uazuay.edu.ec');

Otra alternativa es seleccionar la tabla, dar click derecho y poner


la opción Populate, en donde colocamos Close. Se abre una grilla
en donde el primer botón sobre la tabla nos permite crear nuevas
filas. Al terminar de ingresar la información seleccionamos el
botón Commit para grabar los cambios

Para consumir la base de datos creada se siguen los siguientes pasos:

1. Crear la carpeta assets


En el árbol de Android Studio (vista Android) ir al nodo app. Dar
click con el botón derecho y seleccionar

New>Folder>Assets Folder
En target source set, poner Main (asset queda a nivel de app).

2. Copiar la base de datos (mydb) creada anteriormente y pegar en


la ruta
[Proyectos Android]/app/src/main/assets/
/AndroidStudioProjects/Databases7/app/src/main/assets

3. Verificar si en la aplicación existe el directorio


“data/data/[paquete]/databases”. Si no existe es creado.
String destPath = "/data/data/" + getPackageName() +
"/databases";

File f = new File(destPath);


if (!f.exists())
{ f.mkdirs();
f.createNewFile();

4. Se revisa si existe el archivo MyDB, y si no, se copia la base de


datos desde la carpeta assets.

String destArc = destPath+"/MyDB";


File basedatos = new File(destArc);
if (!basedatos.exists())
CopyDB(getBaseContext().getAssets().open("mydb"), new
FileOutputStream(destPath + "/MyDB"));

El método de copia se implementa de la siguiente manera:

public void CopyDB(InputStream inputStream, OutputStream


outputStream) throws IOException
{ byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0)
{ outputStream.write(buffer, 0, length);}
inputStream.close();
outputStream.close();
}

5. Para probar el cambio, abrimos la base de datos y mostramos los


contactos

db.open();
Cursor c = db.getAllContacts();
if (c.moveToFirst())
{ do { DisplayContact(c);
} while (c.moveToNext());
}
db.close();

public void DisplayContact(Cursor c)


{
Toast.makeText(this, "id: " + c.getString(0) + "\n" + "Name: " +
c.getString(1) + "\n" + "Email: " + c.getString(2),
Toast.LENGTH_LONG).show();}}

Potrebbero piacerti anche