Android desde Cero: Cómo Guardar Datos de la Aplicación de forma Local
Spanish (Español) translation by Rodney Martinez (you can also view the original English article)
Cuando se trata de aplicaciones de datos locales, los desarrolladores Android definitivamente lo echan a perder. Además de dirigir el acceso a las áreas de almacenaje interno y externo del dispositivo de Android, la plataforma Android ofrecer bases de datos SQLite para almacenar datos relacionales y archivos especiales para guardar valores-claves. Es más, las aplicaciones Android también pueden usar bases de datos de otros servicios que ofrecen soporte para NoSQL.
En este tutorial, le mostraré cómo usar todas esas opciones de almacenaje en sus aplicaciones Android. Además, le ayudaré a comprender cómo escoger la opción de almacenaje más adecuada para sus datos.
¿Encuentra que es más fácil aprender con vídeos? Entonces porqué no revisa nuestro curso:
1. Almacenando pares Claves-Valores
Si está buscando una forma rápida de almacenar algunos strings o números, entonces debería de considerar usar el archivo preferences. Las activities de Android y los servicios pueden usar el método getDefaultSharedPreferences()
de la clase PreferenceManager
para conseguir una referencia al objeto SharedPreferences
que puede ser utilizado tanto para leer y escribir en el archivo de preferencias por defecto.
SharedPreferences myPreferences = PreferenceManager.getDefaultSharedPreferences(MyActivity.this);
Para empezar a escribir en el archivo preferencias, usted debe llamar al método edit()
del objeto SharedPreferences
, el cual devuelve un objeto SharedPreferences.Editor
.
SharedPreferences.Editor myEditor = myPreferences.edit();
El objeto SharedPreferences.Editor
tiene varios métodos intuitivos que puede usar para guardar nuevos pares de valores-claves en el archivo preferencias. Por ejemplo, podría usar el método putString()
para poner pares de valores-claves cuyo valor es de tipo String
. De forma similar, usted podría usar el método putFloat()
para poner pares de valores-claves cuyo valor es de tipo float
. El siguiente fragmento de código crea tres pares de valor-clave:
myEditor.putString("NAME", "Alice"); myEditor.putInt("AGE", 25); myEditor.putBoolean("SINGLE?", true);
Una vez que ha añadido todos los pares, usted debe llamar al método commit()
del objeto SharedPreferences.Editor
para hacerlos persistente.
myEditor.commit();
La consulta desde un objeto SharedPreferences
es mucho más fácil. Todo lo que necesita hacer es llamar al método apropiado, es decir, get*()
. Por ejemplo, para conseguir un para de valor-clave cuyo valor es de tipo String
, usted debe llamar al método getString()
. Acá está el fragmento de código que extrae todos los valores que usted añadió anteriormente:
String name = myPreferences.getString("NAME", "unknown"); int age = myPreferences.getInt("AGE", 0); boolean isSingle = myPreferences.getBoolean("SINGLE?", false);
Como puede ver en el código anterior, como segundo parámetro, todos los métodos get*()
esperan un valor por defecto, el cual es el valor que debe ser regresado si la clave no está presente en el archivo preferencias.
Observe que los archivos preferencias están limitados solamente a datos string y primitivos. Si desea guardar tipos de datos más complejos o datos binarios, usted debe escoger una opción diferente de almacenamiento.
2. Usando una Base de Datos SQLite
Cada aplicación Android puede crear y hacer uso de bases de datos SQLite para almacenar grandes cantidades de datos estructurados. Como ya debe saber, SQLite no es solamente peso ligero, sino también muy rápida. Si usted tiene experiencia trabajando con sistema de gestión de bases de datos relacional y, además, está familiarizado con SQL, el cual es la abreviatura para Lenguaje de Consulta Estructurado, el cual es la abreviatura para la Conectividad de Base de Datos Java, está podría ser una opción de almacenamiento preferida.
Para crear un nueva base de datos SQLite, o para abrir una que ya existe, usted puede usar el método openOrCreateDatabase()
dentro de su activity o de su servicio. Como su argumento, debe pasar el nombre de su base de datos y el modo en el cual quiere abrirla. El modo más usado es MODE_PRIVATE
, el cual asegura que la base de datos es accesible solamente par su aplicación. Por ejemplo, así es cómo usted abriría o crearía una base de datos llamada my.db.
SQLiteDatabase myDB = openOrCreateDatabase("my.db", MODE_PRIVATE, null);
Una vez que la base de datos ha sido creada, usted puede usar el método execSQL()
para ejecutar la declaración SQL dentro de él. El siguiente código le muestra como usar la declaración SQL; CREATE TABLE
, para crear una tabla llamada user, la cual tiene tres columnas.
myDB.execSQL( "CREATE TABLE IF NOT EXISTS user (name VARCHAR(200), age INT, is_single INT)" );
Aunque es posible insertar nuevas filas dentro de la tabla usando el método execSQL()
, es mejor usar el método insert()
. El método insert()
espera un objeto ContentValues
que contiene los valores para cada columna de la tabla. Un objeto ContentValue
es muy similar a un objeto Map
y, además, éste contiene pares de valores-claves.
Aquí están dos objetos ContentValues
que puede usar con la tabla user
:
ContentValues row1 = new ContentValues(); row1.put("name", "Alice"); row1.put("age", 25); row1.put("is_single", 1); ContentValues row2 = new ContentValues(); row2.put("name", "Bob"); row2.put("age", 20); row2.put("is_single", 0);
Como podría haber imaginado, las claves que usted pasa el método put()
deben coincidir con los nombres de las columnas en las tablas.
Una vez que su objeto ContentView
está listo, usted puede pasarlos al método insert()
junto con el nombre de la tablar.
myDB.insert("user", null, row1); myDB.insert("user", null, row2);
Para consultar una base de datos, usted puede usar el método rawQuery()
, el cual retorna un objeto Cursor
que contiene los resultados de la consulta.
Cursor myCursor = myDB.rawQuery("select name, age, is_single from user", null);
Un objeto Cursor
puede contener cero o más filas. La forma más fácil de examinar todas sus filas es para llamar su método moveToNext()
dentro del ciclo while
.
Para buscar el valor de una columna individual, usted debe usar métodos tales como getString()
y getInt()
, que esperan el índice de la columna. Por ejemplo, así es como usted extraería todos los valores que inserto en la tabla user
:
while(myCursor.moveToNext()) { String name = myCursor.getString(0); int age = myCursor.getInt(1); boolean isSingle = (myCursor.getInt(2)) == 1 ? true:false; }
Una vez que ha alcanzado todos los resultados de su consulta, asegúrese que llamó al método close()
del objeto Cursor
para publicar todos los recursos que tiene.
myCursor.close();
De manera similar, cuando ha terminado todas las operaciones de su base de datos, no olvide llamar el método close()
del objeto SQLiteDatabase
.
myDB.close();
3. Usando el Almacenamiento Interno
Cada aplicación Android tiene un directorio de almacenamiento privado asociado con ella, en el cual la aplicación puede almacenar texto y archivos binarios. Los archivos dentro de este directorio, no están accesible para el usuario o para otras aplicaciones instaladas en el dispositivo del usuario. Además, son eliminadas de forma automática cuando el usuario desinstala la aplicación.
Antes que pueda usar el directorio de almacenamiento interno, usted debe determinar su ubicación. Para hacer eso, usted puede llamar al método getFilesDir()
, el cual está disponible tanto para las activities como para los servicios.
File internalStorageDir = getFilesDir();
Para conseguir una referencia a un archivo dentro del directorio, usted puede pasar el nombre del archivo junto con la ubicación que usted determino. Por ejemplo, así es cómo usted conseguiría una referencia a un archivo llamado alice.csv.
File alice = new File(internalStorageDir, "alice.csv");
Desde este punto, usted puede usar sus conocimientos en las clases y método de Java I/O para leer o escribir el archivo. El siguiente fragmento de código le muestra como usar un objeto FileOutputStream
y su método write()
para escribir el archivo:
// Create file output stream fos = new FileOutputStream(alice); // Write a line to the file fos.write("Alice,25,1".getBytes()); // Close the file output stream fos.close();
4. Usando Almacenamiento Externo
Debido a que la capacidad del almacenamiento interno de los dispositivos Android está, por lo general, fijo, y a menudo bastante limitado, varios dispositivos Android admiten almacenamiento externo tal como tarjetas desmontables como micro-SD. Recomiendo que use esta opción de almacenamiento para archivos grandes, tales como fotos y vídeos.
A diferencia del almacenamiento interno, el externo no podría estar siempre disponible. Por lo tanto, siempre debe revisar si está montado antes de usarlo. Para hacer eso, use el método getExternalStorageState()
de la clase Environment
.
if(Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED)) { // External storage is usable } else { // External storage is not usable // Try again later }
Una vez que esté seguro que el almacenamiento externo está disponible, usted puede conseguir la ruta al directorio de almacenamiento externo para su aplicación por medio de una llamada al método getExternalFilesDir()
y pasar null
como un argumento. Entonces puede usar la ruta para hacer referencia los archivos dentro del directorio. Por ejemplo, así es cómo haría referencia a un archivo llamado bob.jpg en el directorio de almacenamiento externo de su aplicación:
File bob = new File(getExternalFilesDir(null), "bob.jpg");
Al preguntarle al usuario que le conceda el permiso WRITE_EXTERNAL_STORAGE
, usted puede obtener acceso a todos los archivos del sistema en el almacenamiento externo. Luego puede usar los directorios públicos para almacenar sus fotos, películas y otros archivos. La clase Environment
ofrece un método llamado getExternalStoragePublicDirectory()
para determinar las rutas de esos directorios públicos.
Por ejemplo, al pasar el valor Environment.DIRECTORY_PICTURE
para el método, usted puede determinar la ruta del directorio público en el cual puede almacenar fotos. De manera similar, si usted pasa el valor Environment.DIRECTORY_MOVIES
al método, entonces usted consigue la ruta al directorio público en el cual las películas pueden ser almacenadas.
Así es cómo haría referencia a un archivo llamado bob.jpg en el directorio públicos de imágenes:
File bobInPictures = new File( Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), "bob.jpg" );
Una vez que tiene el objeto File
, nuevamente, puede usar las clases FilesInputStream
y FileOutputStream
para leer y escribir desde éste objeto.
En conclusión
Ahora sabe como sacar el mejor provecho de las opciones de almacenamiento local proporcionadas por Android SDK. Independientemente de las opciones de almacenamiento que usted elija, las operaciones de lectura/escritura pueden ser consumidoras de tiempo si grandes cantidades de datos están involucradas. Por lo tanto, para asegurarse que el hilo UI principal siempre permanece flexible, usted debe considerar la ejecución de las operaciones en diferentes hilos.
Para aprender más acerca de las aplicaciones de almacenamiento de local de datos, consulte la guía oficial de almacenamiento de datos.