Vista rápida de almacenamiento
- Usar preferencias compartidas para datos primitivos
- Usar el almacenamiento interno del dispositivo para datos privados
- Usar el almacenamiento externo para grandes conjuntos de datos que no son privados
- Usar bases de datos SQLite para obtener almacenamiento estructurado
En este documento:
- Cómo utilizar las preferencias compartidas
- Cómo usar el almacenamiento interno
- Cómo usar el almacenamiento externo
- Cómo utilizar las bases de datos
- Cómo usar una conexión de red
Consulta también:
Android ofrece diferentes opciones para que guardes datos persistentes de la aplicación. La solución que elijas depende de tus necesidades específicas; por ejemplo, si los datos deben ser privados para tu aplicación o estar disponibles para otras aplicaciones (y el usuario), y la cantidad de espacio que requieren tus datos.
Tus opciones de almacenamiento de datos son las siguientes:
- Preferencias compartidas
- Almacenamiento de datos primitivos privados en pares clave-valor.
- Almacenamiento interno
- Almacenamiento de datos privados en la memoria del dispositivo.
- Almacenamiento externo
- Almacenamiento de datos públicos en el almacenamiento externo compartido.
- Bases de datos SQLite
- Almacenamiento de datos estructurados en una base de datos privada.
- Conexión de red
- Almacenamiento de datos en la Web mediante tu propio servidor de red.
Android ofrece un método para que expongas tus datos privados a otras aplicaciones, mediante un proveedor de contenido. Un proveedor de contenido es un componente opcional que expone el acceso de lectura y escritura a los datos de tu aplicación, sujetos a las restricciones que desees establecer. Para obtener más información sobre cómo usar los proveedores de contenido, consulta la documentación Proveedores de contenido.
Cómo utilizar las preferencias compartidas
La clase de SharedPreferences ofrece un marco general que te permite
guardar y recuperar pares clave-valor persistentes de tipos de datos primitivos. Puedes usar SharedPreferences para guardar todos los tipos de datos primitivos: booleanos, elementos flotantes, valores enteros, valores largos y
strings. Estos datos se conservarán de una sesión de usuario a otra (incluso si tu aplicación finaliza).
Preferencias del usuario
Las preferencias compartidas no se usan estrictamente para guardar las “preferencias del usuario”, como el tono
que eligió un usuario. Si estás interesado en crear preferencias del usuario para tu aplicación, consulta PreferenceActivity, donde se brinda un marco de actividades para que crees
preferencias del usuario, las cuales se conservarán de manera automática (mediante preferencias compartidas).
Si deseas obtener un objeto de SharedPreferences para tu aplicación, usa uno de
los dos métodos siguientes:
getSharedPreferences(): usa esta opción si necesitas varios archivos de preferencias identificados por nombre, que especifiques con el primer parámetro.getPreferences(): usa esta opción si solo necesitas un archivo de preferencias para tu actividad. Debido a que este será el único archivo de preferencias para tu actividad, no debes proporcionar un nombre.
Para escribir valores, haz lo siguiente:
- Llama a
edit()para obtener unSharedPreferences.Editor. - Agrega valores con métodos, como
putBoolean()yputString(). - Confirma los nuevos valores con
commit().
Para leer valores, usa métodos de SharedPreferences, como getBoolean() y getString().
A continuación, se describe un ejemplo mediante el cual se guarda una preferencia para el modo de presión silencioso en una calculadora:
public class Calc extends Activity {
public static final String PREFS_NAME = "MyPrefsFile";
@Override
protected void onCreate(Bundle state){
super.onCreate(state);
. . .
// Restore preferences
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean silent = settings.getBoolean("silentMode", false);
setSilent(silent);
}
@Override
protected void onStop(){
super.onStop();
// We need an Editor object to make preference changes.
// All objects are from android.context.Context
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("silentMode", mSilentMode);
// Commit the edits!
editor.commit();
}
}
Cómo usar el almacenamiento interno
Puedes guardar archivos directamente en el almacenamiento interno del dispositivo. De forma predeterminada, los archivos que se guardan en el almacenamiento interno son privados para tu aplicación y otras aplicaciones no pueden tener acceso a ellos (tampoco el usuario). Cuando el usuario desinstala tu aplicación, estos archivos se quitan.
Par crear y escribir un archivo privado en el almacenamiento interno, haz lo siguiente:
- Llama a
openFileOutput()mediante el nombre del archivo y el modo de operación. Esto muestra unFileOutputStream. - Realiza operaciones de escritura en el archivo con
write(). - Cierra el flujo con
close().
Por ejemplo:
String FILENAME = "hello_file"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close();
MODE_PRIVATE creará el archivo (o reemplazará un archivo del
mismo nombre) y lo hará privado para tu aplicación. Otros modos disponibles son los siguientes: MODE_APPEND, MODE_WORLD_READABLE y MODE_WORLD_WRITEABLE.
Nota: Las constantes MODE_WORLD_READABLE y MODE_WORLD_WRITEABLE dejaron de estar disponibles desde el nivel de API 17.
A partir de Android N, su uso generará una SecurityException
.
Esto significa que las apps para Android N y versiones posteriores
no pueden compartir archivos privados por nombre y los intentos de compartir una URI de “file://” generará una
FileUriExposedException. Si tu app debe compartir archivos
privados con otras apps, podrá hacer uso de un FileProvider junto
con FLAG_GRANT_READ_URI_PERMISSION.
Consulta también Cómo compartir archivos.
Para leer un archivo desde el almacenamiento interno, haz lo siguiente:
- Llama a
openFileInput()y usa el nombre del archivo que deseas leer. Esto muestra unFileInputStream. - Lee los bytes del archivo con
read(). - A continuación, cierra el flujo con
close().
Sugerencia: Si deseas guardar un archivo estático en tu aplicación en
el momento de la compilación, guarda el archivo en el directorio de tu proyecto res/raw/. Puedes abrirlo con
openRawResource() mediante el ID de recursos de
R.raw.<filename>. Este método muestra un InputStream
que puedes usar para leer el archivo (pero no puedes realizar operaciones de escritura en el archivo original).
Cómo guardar archivos de caché
Si deseas almacenar en caché algunos datos, en lugar de guardarlos de manera continua debes usar getCacheDir() para abrir un File que represente el directorio interior donde tu aplicación debe guardar
los archivos de caché temporales.
Cuando el dispositivo tenga poco espacio de almacenamiento interno, Android podrá quitar estos archivos de caché para recuperar espacio. Sin embargo, no debes confiar en que el sistema borrará estos archivos. Siempre deberás encargarte de mantener los archivos de caché y mantenerlos dentro de un límite de espacio razonable, por ejemplo, 1 MB. Cuando el usuario desinstala tu aplicación, estos archivos se quitan.
Otros métodos útiles
getFilesDir()- Obtiene la ruta de acceso absoluta al directorio de sistema de archivos donde se guardan tus archivos internos.
getDir()- Crea un directorio (o abre uno existente) dentro de tu espacio de almacenamiento interno.
deleteFile()- Quita un archivo guardado en el almacenamiento interno.
fileList()- Muestra un conjunto de archivos que tu aplicación guarda actualmente.
Cómo usar el almacenamiento externo
Todo dispositivo compatible con Android admite un “almacenamiento externo” compartido, que puedes usar para guardar archivos. Puede ser un medio de almacenamiento extraíble (como una tarjeta SD) o un medio almacenamiento interno (no extraíble). Los archivos guardados en el almacenamiento externo pueden ser leídos por cualquier usuario y este puede modificarlos cuando habilita el almacenamiento masivo de USB para transferir archivos a una computadora.
Advertencia: Es posible que el almacenamiento externo deje de estar disponible si el usuario conecta el almacenamiento externo a una computadora o quita el medio, y no se implementan medidas de seguridad en los archivos que guardes en el almacenamiento externo. Todas las aplicaciones pueden realizar operaciones de lectura y escritura en los archivos guardados en el almacenamiento externo y el usuario puede quitarlos.
Uso del acceso a directorios determinados
En Android 7.0 o versiones posteriores, si necesitas acceder a un directorio específico en el almacenamiento externo, usa el acceso a directorios determinados. El acceso a directorios determinados simplifica la manera en que tu aplicación accede a los directorios de almacenamiento externo estándar, como el directorioPictures, y brinda una IU de permisos
simples que detalla claramente el directorio al cual la aplicación
solicita acceso. Para obtener más información sobre el acceso a directorios determinados, consulta
Uso del
acceso a directorios determinados.
Cómo obtener acceso al almacenamiento externo
A fin de realizar operaciones de lectura escritura en archivos en el almacenamiento externo, tu app debe obtener los permisos
de sistema del
READ_EXTERNAL_STORAGE
o WRITE_EXTERNAL_STORAGE. Por ejemplo:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
Si necesitas leer y escribir archivos, solo debes solicitar el permiso de
WRITE_EXTERNAL_STORAGE, ya que este
también requiere implícitamente acceso de lectura.
Nota: A partir de Android 4.4, estos permisos no son necesarios si deseas leer o escribir archivos que solamente son privados para tu app. Para obtener más información, consulta la sección siguiente sobre cómo guardar archivos privados para la aplicación.
Cómo comprobar la disponibilidad de medios
Antes de hacer cualquier trabajo con el almacenamiento externo, siempre debes llamar a getExternalStorageState() a fin de comprobar si el medio está disponible. El
medio podría conectarse a una computadora o faltar, ser de solo lectura o hallarse en algún otro estado. Por ejemplo,
a continuación, se describen algunos métodos que puedes usar para comprobar la disponibilidad:
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
El método getExternalStorageState() muestra otros estados que
tal vez desees comprobar, como el uso compartido de los medios (su conexión a una computadora), la falta
total de estos, su eliminación incorrecta, etc. Puedes usar estas opciones para transmitir al usuario más información
cuando tu aplicación necesite acceder al medio.
Cómo guardar archivos que se pueden compartir con otras apps
Cómo ocultar tus archivos del escáner multimedia
Incluye un archivo vacío con el nombre .nomedia en tu directorio de archivos externos (ten en cuenta el prefijo del
punto en el nombre del archivo). Esto evita que el escáner multimedia lea tus archivos del
medio y los ponga a disposición de otras apps mediante el MediaStore
proveedor de contenido. Sin embargo, si tus archivos son verdaderamente privados para tu app, deberías
guardarlos en un directorio privado de esta.
Por lo general, los nuevos archivos que el usuario puede obtener mediante tu app deben guardarse en una ubicación “pública”
del dispositivo, en la cual otras apps puedan acceder a ellos y el usuario pueda copiarlos fácilmente desde el
dispositivo. Cuando lo hagas, debes usar uno de los directorios públicos compartidos, como Music/, Pictures/ y Ringtones/.
Para obtener un File que represente el directorio público adecuado, llama a getExternalStoragePublicDirectory() y usa el tipo de directorio que desees, como
DIRECTORY_MUSIC, DIRECTORY_PICTURES,
DIRECTORY_RINGTONES u otros. Al guardar tus archivos en el
directorio de tipos de medios correspondiente,
el escáner multimedia del sistema puede categorizar adecuadamente tus archivos en el sistema (por
ejemplo, los tonos se muestran en la configuración del sistema como tonos, no como música).
Por ejemplo, a continuación, se describe un método que crea un directorio para un nuevo álbum de fotografías en el directorio público de imágenes:
public File getAlbumStorageDir(String albumName) {
// Get the directory for the user's public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
Cómo guardar archivos privados para la app
Si gestionas archivos que no deseas que otras apps usen
(como texturas gráficas o efectos de sonido usados solo por tu app), debes usar
un directorio de almacenamiento privado en el almacenamiento externo; para ello, llama a getExternalFilesDir().
Este método también toma un argumento de type para especificar el tipo de subdirectorio
(por ejemplo, DIRECTORY_MOVIES). Si necesitas un directorio de medios
específicos, usa null para obtener
el directorio raíz del directorio privado de tu app.
A partir de Android 4.4, para realizar operaciones de lectura o escritura de archivos en los directorios
privados de tu app no se requieren los permisos de READ_EXTERNAL_STORAGE
ni
WRITE_EXTERNAL_STORAGE. Por eso, puedes definir que el permiso deba solicitarse solo en las versiones anteriores
de Android; para ello, agrega el atributo maxSdkVersion
:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
Nota:
cuando el usuario desinstala tu aplicación, este directorio y todo su contenido se quitan.
Además, el escáner multimedia no lee los archivos de estos directorios, por lo cual no es posible acceder
a ellos desde el proveedor de contenido MediaStore. Por ello, no debes
usar estos directorios para los medios que, en última instancia, pertenecen al usuario, como fotografías
tomadas o editadas con tu app, o música que el usuario adquirió a través de ella. Esos
archivos deben guardarse en directorios públicos.
A veces, un dispositivo que asignó una partición de la
memoria interna para usarse como almacenamiento externo también puede ofrecer una ranura para tarjeta SD.
Cuando ese dispositivo se ejecuta en Android 4.3 o versiones anteriores, el método de getExternalFilesDir() brinda
acceso solo a la partición interna y tu aplicación no puede realizar operaciones de lectura o escritura en la tarjeta SD.
Sin embargo, a partir de Android 4.4 puedes acceder a ambas ubicaciones llamando a
getExternalFilesDirs(),
lo cual muestra un conjunto de File con entradas a cada ubicación. La primera entrada del conjunto se considera
el almacenamiento externo principal y debes usar esa ubicación, a menos que esté llena o
no se encuentre disponible. Si deseas poder acceder a las dos ubicaciones posibles y además brindar compatibilidad con Android
4.3 y las versiones anteriores, usa el
método estático de la biblioteca de compatibilidad, ContextCompat.getExternalFilesDirs(). Esto también muestra un arreglo de File, pero siempre incluye solo una entrada en Android 4.3 y versiones anteriores.
Advertencia Si bien el
proveedor de contenido de MediaStore no puede acceder a los directorios proporcionados por getExternalFilesDir() y getExternalFilesDirs(), otras apps con el permiso de READ_EXTERNAL_STORAGE pueden acceder a todos los archivos del almacenamiento
externo, incluidos estos. Si necesitas restringir el acceso a tus archivos por completo, en su lugar debes
escribir tus archivos en el almacenamiento interno.
Cómo guardar archivos de caché
Para abrir un File que represente el
directorio de almacenamiento en caché externo en el cual debes guardar los archivos de caché, llama a getExternalCacheDir(). Si el usuario desinstala tu
aplicación, estos archivos se borrarán automáticamente.
En un caso similar al de ContextCompat.getExternalFilesDirs(), mencionado anteriormente, también puedes acceder a un directorio de caché en
un medio de almacenamiento externo secundario (si está disponible) llamando a
ContextCompat.getExternalCacheDirs().
Sugerencia: A fin de preservar el espacio del archivo y mantener el rendimiento de tu app, es importante que administres cuidadosamente tus archivos de caché y quites aquellos que ya no sean necesarios durante todo el ciclo de vida de tu app.
Cómo utilizar las bases de datos
Android ofrece compatibilidad total con las bases de datos SQLite. Cualquier clase dentro de la aplicación, no fuera de ella, será posible acceder por nombre a cualquier base de datos que crees.
El método recomendado para crear una nueva base de datos SQLite consiste en crear una subclase de SQLiteOpenHelper y anular el método de onCreate(), en el cual
puedes ejecutar un comando de SQLite a fin de crear tablas en la base de datos. Por ejemplo:
public class DictionaryOpenHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 2;
private static final String DICTIONARY_TABLE_NAME = "dictionary";
private static final String DICTIONARY_TABLE_CREATE =
"CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" +
KEY_WORD + " TEXT, " +
KEY_DEFINITION + " TEXT);";
DictionaryOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DICTIONARY_TABLE_CREATE);
}
}
Puedes obtener una instancia de tu implementación de SQLiteOpenHelper
mediante el constructor definido. Para realizar operaciones de lectura y escritura en la base de datos, llama a
getWritableDatabase() y getReadableDatabase(), respectivamente. Estas dos opciones muestran un objeto
SQLiteDatabase que representa la base de datos y
proporciona métodos para las operaciones de SQLite.
Android no impone ninguna limitación más allá de los conceptos de SQLite estándares. No recomendamos
incluir un campo clave-valor de aumento automático que pueda usarse como ID único para
buscar un registro rápidamente. Esto no se requiere en el caso de los datos privados, pero si
implementas un proveedor de contenido,
debes incluir un ID único mediante la
constante BaseColumns._ID.
Puedes ejecutar consultas de SQLite a través de los métodos de SQLiteDatabase
query(), los cuales aceptan varios parámetros de consulta, como la tabla para consulta,
la proyección, la selección, las columnas y el agrupamiento, entre otros. En el caso de consultas complejas, como
aquellas que requieren alias de columna, debes usar
SQLiteQueryBuilder, el cual ofrece
diferentes métodos convenientes para crear consultas.
Cada consulta de SQLite mostrará un Cursor que señale todas las filas
que encontró la consulta. El Cursor siempre es el mecanismo con el cual
puedes navegar por los resultados de una consulta de la base de datos, y leer filas y columnas.
Para hallar ejemplos de apps que demuestren la forma de usar las bases de datos SQLite en Android, consulta las aplicaciones del bloc de notas y el diccionario de búsquedas.
Depuración de la base de datos
El Android SDK incluye una herramienta de base de datos sqlite3 que te permite explorar
contenido de tablas, ejecutar comandos de SQL y llevar a cabo otras funciones útiles en las bases de datos
SQLite. Consulta Cómo examinar las bases de datos sqlite3
desde una interfaz remota para aprender cómo usar esta herramienta.
Cómo usar una conexión de red
Puedes usar la red (cuando se encuentre disponible) para almacenar y obtener datos de tus propios servicios basados en la Web. Para realizar operaciones en la red, usa las clases de los siguientes paquetes: