X

Guardar Archivos

Android utiliza un sistema de archivos que es similar al sistema de archivos de otras plataformas. Esta lección describe cómo trabajar con el sistema de archivos de Android para leer y escribir archivos con las APIs File.

Un objeto File se adapta para leer o escribir grandes cantidades de datos de principio a fin sin saltarse ningún elemento. Por ejemplo, es bueno para los archivos de imágenes o cualquier archivo intercambiado en una red.

Esta lección te muestra cómo realizar las tareas básicas relacionadas con los archivos en tu app. La lección asume que te has familiarizado con el sistema de archivos Linux y las APIs estándar de archivos entrada/salida java.io.

Elegir entre el Almacenamiento Interno o Externo

Todos los dispositivos Android tienen dos áreas de almacenamiento: internasexternas. Esos nombres vienen de las primeras etapas de Android, cuando la mayoría de los dispositivos ofrecían memoria interna no volátil, almacenamiento interno, y elementos de almacenamiento externo como pueden ser las las tarjetas de memoria SD. Algunos dispositivos dividen el espacio de almacenamiento permanente dentro de particiones internas y externas, así incluso sin un medio de almacenamiento extraíble hay dos espacios de almacenamiento y el comportamiento de la API es el mismo independientemente de que haya un almacenamiento externo o no. Las listas siguientes son un resumen de la información de estos tipos de espacios de almacenamiento.

Almacenamiento Interno:

  • Siempre está disponible.
  • Los archivos guardados aquí son accesibles únicamente por tu app por defecto.
  • Cuando el usuario desinstala tu app, el sistema elimina todos los archivos de tu app del almacenamiento interno.

El almacenamiento interno es la mejor opción cuando quieres asegurarte de que tanto el usuario u otras apps pueden acceder a tus archivos.

Almacenamiento Externo:

  • No está siempre disponible ya que el usuario puede montar el almacenamiento externo mediante una memoria USB y en algunos casos puede extraerla desde el dispositivo.
  • Todos pueden leer los archivos, es decir, si los guardas aquí pueden estar fuera de tu control.
  • Cuando el usuario desinstala tu app, el sistema elimina los archivos de la app únicamente si los guardaste en un directorio utilizando getExternalFilesDir().

El almacenamiento externo es el mejor lugar para los archivos que no necesitan restricciones de acceso y para archivos que quieres que se puedan compartir con otras apps o que el usuario pueda acceder a ellos desde un ordenador.

Consejo: Aunque las apps se instalan en el almacenamiento interno por defecto, puedes especificar el atributo android:installLocation en el archivo manifest para que tu app pueda ser instalada en un almacenamiento externo. Los usuarios aprecian esta opción cuando el archivo APK ocupa bastante y ellos utilizan un espacio de almacenamiento externo más grande que el almacenamiento interno. Para más información visita Ubicación de la Instalación de App.

Obtener Permisos para el Almacenamiento Externo

Para poder escribir en el almacenamiento externo, necesitas hacer una petición del permiso WRITE_EXTERNAL_STORAGE en el archivo manifest:

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

Precaución: Actualmente, las apps tienen la habilidad de leer el almacenamiento externo sin requerir de un permiso especial. Sin embargo, esto cambiará en una versión de Android futura. Si necesitas que tu app lea el almacenamiento externo (sin la necesidad de escribir sobre él), entonces necesitarás declarar el permiso READ_EXTERNAL_STORAGE. Para asegurarte de que tu app continua funcionando como esperas, deberías declarar este permiso ya, antes de que el cambio tome efecto.

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
</manifest>

Sin embargo, si tu app utiliza el permiso WRITE_EXTERNAL_STORAGE, es implícito que tiene que tener permiso para leer el almacenamiento externo sin declarar expresamente el permiso de lectura.

No necesitas ningún permiso para guardar archivos en el almacenamiento interno. Tu aplicación siempre tiene permiso para leer y escribir archivos en su directorio del almacenamiento interno.

Guardar un Archivo en el Almacenamiento Interno

Cuando guardas un archivo en el almacenamiento interno, puedes obtener el directorio como un File mediante uno de los dos métodos siguientes:

  • getFilesDir(): Devuelve un File que representa un directorio interno de tu app.
  • getCacheDir(): Devuelve un File que representa un directorio interno de tu app para archivos temporales en cache. Asegúrate de eliminar cada uno de los archivos una vez que no se necesiten más e implementar un límite de espacio razonable para la cantidad de memoria que utilices en dicho momento, como puede ser 1MB. Si el sistema se encuentra con la situación de quedarse sin espacio, puede eliminar los archivos en cache sin riesgos.

Para crear un nuevo archivo en uno de esos directorios, puedes utilizar el constructor File(), pasando el File proporcionado por uno de los métodos anteriores que especifican el directorio de almacenamiento interno de tu app. Por ejemplo:

File file = new File(context.getFilesDir(), filename);

Alternativamente, puedes utilizar openFileOutput() para obtener un FileOutputStream que escribirá un archivo en tu directorio interno. Por ejemplo, así puedes escribir cualquier texto en un archivo:

String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;

try {
  outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
  outputStream.write(string.getBytes());
  outputStream.close();
} catch (Exception e) {
  e.printStackTrace();
}

O, si necesitas guardar en cache algunos archivos, deberías utilizar en su lugar createTempFile(). Por ejemplo, el siguiente método extrae el nombre del archivo desde una URL y crea un archivo con ese nombre en el directorio interno de la cache de tu app.

public File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(fileName, null, context.getCacheDir());
    catch (IOException e) {
        // Error while creating file
    }
    return file;
}

Nota: El almacenamiento interno de tu app se especifica mediante el nombre del paquete de la misma en una ubicación especial dentro del sistema de archivos de Android. Técnicamente, otra app puede leer tus archivos internos si tu lo permites utilizando cierto modo de archivos para que sean de lectura. Sin embargo, la otra app necesitaría también conocer el nombre del paquete de tu app y los nombres de los archivos. Las otras apps no pueden buscar en tus directorios internos y no tienen acceso para leer o escribir a menos que tu lo especifiques explícitamente. Si tu utilizas MODE_PRIVATE, tus archivos dentro del almacenamiento interno jamás serán accesibles por otras apps.

Guardar un Archivo en el Almacenamiento Externo

Ya que el almacenamiento externo puede no estar disponible, por ejemplo el usuario ha podido retirar la tarjeta SD, deberías siempre verificar que el mismo se encuentra disponible antes de intentar acceder a él. Puedes comprobar el estado del almacenamiento externo mediante getExternalStorageState(). Si el estado es MEDIA_MOUNTED, entonces puedes leer y escribir tus archivos. Por ejemplo, los siguientes métodos son útiles para determinar la disponibilidad del almacenamiento:

/* 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;
}

Aunque el almacenamiento externo se pueda modificar por el usuario u otras apps, hay dos categorías de archivos que puedes guardar en el mismo:

  • Archivos públicos: Archivos que estarías libremente disponible al resto de apps y al usuario. Cuando el usuario desinstala tu app, esos archivos permanecen disponibles. Por ejemplo, las fotos capturadas por tu app u otros archivos descargados.
  • Archivos privados: Los archivos que pertenecen únicamente a tu app y deberían eliminarse cuando el usuario desinstale tu app. Aunque estos archivos son técnicamente accesibles por el usuario y otras apps ya que se encuentran en el almacenamiento externo, hay archivos que realmente no proporcionan algún tipo de valor fuera de tu app. Cuando el usuario la desinstala, el sistema elimina todos los archivos de tu app dentro del directorio externo privado de la misma. Por ejemplo, los recursos adicionales descargados por tu app o archivos multimedia temporales.

Si quieres guardar los archivos públicos en el almacenamiento externo, utiliza el siguiente método:

getExternalStoragePublicDirectory();

Con dicho método obtienes un File que representa el directorio del almacenamiento externo. El método toma un argumento que especifica el tipo de archivo que quieres guardar y así que se puedan organizar de forma lógica junto a otros archivos públicos, como puede ser DIRECTORY_MUSIC o DIRECTORY_PICTURES. Por ejemplo:

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;
}

Si quieres guardar archivos que son privados, puedes obtener el directorio mediante getExternalFilesDir(), y dándole un nombre indicado del tipo de directorio que te gustaría que fuese. Cada directorio que se crea de esta forma se añade al directorio padre que encapsula todo el almacenamiento externo de tu app, el cual el sistema elimina cuando el usuario desinstala tu app.

Por ejemplo, este método te permite crear un directorio para un álbum de fotos:

public File getAlbumStorageDir(Context context, String albumName) {
    // Get the directory for the app's private pictures directory. 
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

Si ninguno de los subdirectorios predefinidos encajan con el tipo de tus archivos, puedes utilizar getExternalFilesDir() e indicarle null. Esto devuelve el directorio raíz del directorio privado de tu app en el almacenamiento externo.

Recuerda que getExternalFilesDir() crea un directorio dentro de un directorio que se elimina cuando el usuario desinstala tu app. Si los archivos que guardas permanecen disponibles después de que el usuario desinstale tu app, como puede ser las fotos que haya capturado a través de tu app, deberías en su lugar utilizar el siguiente método:

getExternalStoragePublicDirectory().

Independientemente de que utilices el método anterior para los archivos que se comparten o getExternalFilesDir() para archivos que son privados, es importante que utilices los nombres de directorio proporcionados por la API como puede ser DIRECTORY_PICTURES. Esos nombres de directorio aseguran que los archivos se traten correctamente por el sistema. Por ejemplo, los archivos guardados en DIRECTORY_RINGTONES se clasificarán por el sistema como sonidos de llamada en lugar de música.

Comprobar el Almacenamiento Disponible

Si conoces el tamaño de los datos que quieres guardar, puedes averiguar si hay suficiente espacio disponible sin causar una IOException mediante getFreeSpace()getTotalSpace(). Esos métodos proporcional el espacio disponible y el espacio total de un volumen de almacenamiento, respectivamente. Esta información también es útil para evitar llenar el volumen de almacenamiento por encima de cierto umbral.

Sin embargo, el sistema no garantiza que puedas escribir tantos bytes como se indica mediante getFreeSpace(). Si el número obtenido es unos MB superior al tamaño de datos que quieres guardar, o si el sistema se encuentra a menos de un 90% de de la capacidad, es probable que puedas proceder de forma segura. De otro modo, probablemente no deberías escribir en dicho volumen de almacenamiento.

Nota: No es necesario comprobar la cantidad disponible de espacio antes de guardar un archivo. Puedes en su lugar tratar de escribir el archivo directamente, pero entonces tienes que capturar una IOException si esta ocurriese. Puedes necesitar hacer esto si no conoces cuanto espacio necesitas exactamente. Por ejemplo, si quieres cambiar el tipo de codificación de un archivo, como puede ser guardar una imagen PNG a JPEG.

Eliminar un Archivo

Deberías siempre eliminar los archivos que no necesitarás más. La forma más sencilla de eliminar un archivo que se encuentra abierto es mediante delete():

myFile.delete();

Si el archivo se guarda en el almacenamiento interno, puedes también obtener el Context para localizar y eliminar un archivo mediante deleteFile():

myContext.deleteFile(fileName);

Nota: Cuando el usuario desinstala tu app, el sistema Android elimina:

  • Todos los archivos que hayas guardado en el almacenamiento interno.
  • Todos los archivos que hayas guardado en el almacenamiento externo utilizando getExternalFIlesDir().

Sin embargo, deberías manualmente eliminar todos los archivos en cache creados con getCacheDir() regularmente cuando no los necesites.

Puedes ver más información sobre Guardar Archivos en la página oficial de Google en inglés pulsando aquí.

Los comentarios de Disqus están cargando....