Casos prácticos y prácticas recomendadas de almacenamiento en Android

Con el objetivo de darles a los usuarios más control sobre sus archivos y acotar el desorden, en Android 10 se introdujo un nuevo paradigma de almacenamiento para apps llamado almacenamiento específico. El almacenamiento específico cambia la forma en la que las apps almacenan los archivos y acceden a ellos en el almacenamiento externo de un dispositivo. Si deseas migrar tu app para admitir el almacenamiento específico, sigue las prácticas recomendadas para casos de uso de almacenamiento comunes que se describen en esta guía. Se organizan los casos de uso en dos categorías: control de archivos multimedia y control de archivos que no son multimedia.

Para obtener más información sobre almacenamiento de archivos y acceso a ellos en Android, consulta las guías de capacitación sobre almacenamiento.

Cómo controlar archivos multimedia

En esta sección se describen algunos de los casos de uso comunes para controlar archivos multimedia (archivos de video, imágenes y audio) y se explica el enfoque general que tu app puede usar. En la siguiente tabla, se resume cada uno de estos casos de uso y se incluyen vínculos a cada una de las secciones que contienen más detalles.

Caso de uso Resumen
Cómo mostrar todos los archivos de imagen o video Usa el mismo enfoque para todas las versiones de Android.
Cómo mostrar imágenes o videos de una carpeta en particular Usa el mismo enfoque para todas las versiones de Android.
Cómo acceder a la información de ubicación desde las fotos Usa un enfoque si tu app emplea el almacenamiento específico y otro en caso contrario.
Cómo definir la ubicación del almacenamiento para las descargas nuevas Usa un enfoque si tu app emplea el almacenamiento específico y otro en caso contrario.
Cómo exportar archivos multimedia de usuarios a un dispositivo Usa el mismo enfoque para todas las versiones de Android.
Cómo modificar o borrar varios archivos multimedia en una sola operación Usa un enfoque para Android 11. En Android 10, inhabilita el almacenamiento específico y usa el enfoque para Android 9 y versiones anteriores.
Cómo importar una sola imagen que ya existe Usa el mismo enfoque para todas las versiones de Android.
Cómo capturar una sola imagen Usa el mismo enfoque para todas las versiones de Android.
Cómo compartir archivos multimedia con otras apps Usa el mismo enfoque para todas las versiones de Android.
Cómo compartir archivos multimedia con una app específica Usa el mismo enfoque para todas las versiones de Android.
Cómo acceder a archivos desde código o bibliotecas que usan rutas de archivos directas Usa un enfoque para Android 11. En Android 10, inhabilita el almacenamiento específico y usa el enfoque para Android 9 y versiones anteriores.

Cómo mostrar archivos de imagen o video de varias carpetas

Busca una colección multimedia con la API de query(). Para ordenar o filtrar los archivos multimedia, ajusta los parámetros projection, selection, selectionArgs y sortOrder.

Cómo mostrar imágenes o videos de una carpeta en particular

Usa el siguiente enfoque:

  1. Sigue las prácticas recomendadas que se describen en Cómo solicitar permisos de la app y solicita el permiso READ_EXTERNAL_STORAGE.
  2. Recupera archivos multimedia basados en el valor de MediaColumns.DATA, que contiene la ruta absoluta del sistema de archivos hasta el elemento multimedia del disco.

Nota: Cuando accedes a un archivo multimedia existente, puedes usar el valor de la columna DATA en tu lógica. Esto se debe a que este valor tiene una ruta de archivo válida. Sin embargo, no debes asumir que el archivo siempre está disponible. Prepárate para controlar cualquier error de E/S basado en archivos que se pueda producir.

Por otro lado, para crear o actualizar un archivo multimedia, no uses la columna DATA. En su lugar, usa las columnas DISPLAY_NAME y RELATIVE_PATH.

Cómo acceder a la información de ubicación desde las fotos

Si tu app usa almacenamiento específico, sigue los pasos de la sección Información de ubicación en fotografías de la guía de almacenamiento de contenido multimedia.

Cómo definir la ubicación del almacenamiento para las descargas nuevas

Si tu app usa almacenamiento específico, ten en cuenta la ubicación en la que eliges almacenar los archivos multimedia que descargas.

Si otras apps requieren acceso a los archivos, te recomendamos usar colecciones multimedia bien definidas para las descargas o las colecciones de documentos.

En Android 11 y versiones posteriores, otras apps no pueden acceder a los archivos del directorio externo específico de la app, incluso si usas DownloadManager para recuperar estos archivos.

Cómo exportar archivos multimedia de usuarios a un dispositivo

Define una ubicación predeterminada adecuada para almacenar archivos multimedia de usuarios:

Cómo modificar o borrar varios archivos multimedia en una sola operación

Incorpora lógica basada en las versiones de Android en las que se ejecuta tu app.

Si se ejecuta en Android 11

Usa el siguiente enfoque:

  1. Crea un intent pendiente para la solicitud de escritura o eliminación de tu app con MediaStore.createWriteRequest() o MediaStore.createTrashRequest() y, luego, solicita al usuario permiso para invocar ese intent y editar un conjunto de archivos.
  2. Evalúa la respuesta del usuario:

    • Si se otorgó el permiso, continúa con la operación de modificación o eliminación.
    • Si no se otorgó el permiso, explícale al usuario por qué la función de tu app necesita el permiso.

Obtén más información para administrar grupos de archivos multimedia con estos métodos disponibles en Android 11 y versiones posteriores.

Si se ejecuta en Android 10

Si tu app está orientada a Android 10 (nivel de API 29), inhabilita el almacenamiento específico y continúa usando el enfoque para Android 9 y versiones anteriores para realizar esta operación.

Si se ejecuta en Android 9 o versiones anteriores

Usa el siguiente enfoque:

  1. Sigue las prácticas recomendadas que se describen en Cómo solicitar permisos de la app y solicita el permiso WRITE_EXTERNAL_STORAGE.
  2. Usa la API de MediaStore para modificar o borrar los archivos multimedia.

Cómo importar una sola imagen que ya existe

Si deseas importar una sola imagen que ya existe (por ejemplo, para usarla como foto del perfil de un usuario), tu app puede usar su propia IU en la operación, o bien el selector del sistema.

Cómo presentar tu propia interfaz de usuario

Usa el siguiente enfoque:

  1. Sigue las prácticas recomendadas que se describen en Cómo solicitar permisos de la app y solicita el permiso READ_EXTERNAL_STORAGE.
  2. Usa la API de query() para buscar en una colección de archivos multimedia.
  3. Muestra los resultados en la IU personalizada de tu app.

Cómo usar el selector del sistema

Usa el intent ACTION_GET_CONTENT, que le pide al usuario que elija una imagen para importar.

Si deseas filtrar los tipos de imágenes que el selector del sistema presenta al usuario, puedes usar setType() o EXTRA_MIME_TYPES.

Cómo capturar una sola imagen

Si deseas capturar una sola imagen para usarla en tu app (por ejemplo, como foto del perfil de un usuario), usa el intent ACTION_IMAGE_CAPTURE para pedirle al usuario que tome una foto con la cámara del dispositivo. El sistema almacena la foto capturada en la tabla MediaStore.Images.

Cómo compartir archivos multimedia con otras apps

Usa el método insert() para agregar directamente registros a MediaStore. Para obtener más información, consulta la sección Cómo agregar un elemento de la guía de almacenamiento de contenido multimedia.

Cómo compartir archivos multimedia con una app específica

Usa el componente FileProvider de Android, como se describe en la guía Cómo configurar el uso compartido de archivos.

Cómo acceder a archivos desde código o bibliotecas que usan rutas de archivos directas

Incorpora lógica basada en las versiones de Android en las que se ejecuta tu app.

Si se ejecuta en Android 11

Usa el siguiente enfoque:

  1. Sigue las prácticas recomendadas que se describen en Cómo solicitar permisos de la app y solicita el permiso READ_EXTERNAL_STORAGE.
  2. Accede a los archivos mediante rutas de acceso directas.

Si quieres obtener más información, consulta la sección para abrir archivos multimedia con rutas de acceso a archivos directas.

Si se ejecuta en Android 10

Si tu app está orientada a Android 10 (nivel de API 29), inhabilita el almacenamiento específico y continúa usando el enfoque para Android 9 y versiones anteriores para realizar esta operación.

Si se ejecuta en Android 9 o versiones anteriores

Usa el siguiente enfoque:

  1. Sigue las prácticas recomendadas que se describen en Cómo solicitar permisos de la app y solicita el permiso WRITE_EXTERNAL_STORAGE.
  2. Accede a los archivos mediante rutas de acceso directas.

Cómo controlar archivos que no son multimedia

En esta sección se describen algunos de los casos de uso comunes para controlar archivos que no son multimedia y se explica el enfoque general que tu app puede usar. En la siguiente tabla, se resume cada uno de estos casos de uso y se incluyen vínculos a cada una de las secciones que contienen más detalles.

Caso de uso Resumen
Cómo abrir un archivo de documento Usa el mismo enfoque para todas las versiones de Android.
Cómo escribir en archivos de volúmenes de almacenamiento secundario Usa un enfoque para Android 11. Usa un enfoque diferente para las versiones anteriores de Android.
Cómo migrar archivos existentes desde una ubicación de almacenamiento heredado Migra los archivos al almacenamiento específico cuando sea posible. Inhabilita el almacenamiento específico para Android 10 cuando sea necesario.
Cómo compartir contenido con otras apps Usa el mismo enfoque para todas las versiones de Android.
Cómo almacenar en caché archivos que no son multimedia Usa el mismo enfoque para todas las versiones de Android.
Cómo exportar archivos que no son multimedia a un dispositivo Usa un enfoque si tu app emplea el almacenamiento específico y otro en caso contrario.

Cómo abrir un archivo de documento

Usa el intent ACTION_OPEN_DOCUMENT para pedirle al usuario que elija un archivo para abrir mediante el selector del sistema. Si deseas filtrar los tipos de archivos que el selector del sistema presenta al usuario, puedes usar setType() o EXTRA_MIME_TYPES.

Por ejemplo, puedes usar el siguiente código para encontrar todos los archivos PDF, DDT y TXT:

Kotlin

startActivityForResult(
        Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "*/*"
            putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
                    "application/pdf", // .pdf
                    "application/vnd.oasis.opendocument.text", // .odt
                    "text/plain" // .txt
            ))
        },
        REQUEST_CODE
      )

Java

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
                "application/pdf", // .pdf
                "application/vnd.oasis.opendocument.text", // .odt
                "text/plain" // .txt
        });
        startActivityForResult(intent, REQUEST_CODE);

Cómo escribir en archivos de volúmenes de almacenamiento secundario

Los volúmenes de almacenamiento secundario incluyen tarjetas SD. Puedes acceder a la información sobre un volumen de almacenamiento determinado mediante la clase StorageVolume.

Incorpora lógica según la versión de Android en las que se ejecuta tu app.

Si se ejecuta en Android 11

Usa el siguiente enfoque:

  1. Usa el modelo de almacenamiento específico.
  2. Orienta la app a Android 10 (nivel de API 29) o versiones anteriores.
  3. Declara el permiso WRITE_EXTERNAL_STORAGE.
  4. Realiza uno de los siguientes tipos de acceso:
    • Accede a archivos con la API de MediaStore.
    • Acceso directo a la ruta del archivo con APIs como File o fopen()

Si se ejecuta en versiones anteriores

Usa el framework de acceso a almacenamiento, que permite a los usuarios seleccionar la ubicación en un volumen de almacenamiento secundario en el que tu app puede escribir el archivo.

Cómo migrar archivos existentes desde una ubicación de almacenamiento heredado

Se considera que un directorio es una ubicación de almacenamiento heredado si no es un directorio específico de la app o un directorio público compartido. Si tu app crea o consume archivos en una ubicación de almacenamiento heredado, te recomendamos que migres los archivos a ubicaciones a las que se pueda acceder con almacenamiento específico y realizar los cambios necesarios para trabajar con archivos en este tipo de almacenamiento.

Cómo mantener el acceso a la ubicación de almacenamiento heredado para la migración de datos

Tu app debe mantener el acceso a la ubicación de almacenamiento heredado para migrar cualquier archivo de apps a ubicaciones a las que se pueda acceder con almacenamiento específico. El enfoque que debes usar depende del nivel de la API de destino de tu app.

Si tu app se orienta a Android 11
  1. Establece la marca preserveLegacyExternalStorage en true a fin de conservar el modelo de almacenamiento heredado, de modo que tu app pueda migrar los datos de un usuario cuando este actualice a la nueva versión orientada a Android 11.

  2. Para continuar, inhabilita el almacenamiento específico de modo que tu app pueda seguir accediendo a tus archivos en la ubicación de almacenamiento heredado, en dispositivos Android 10.

Si tu app se orienta a Android 10

Inhabilita el almacenamiento específico para que sea más fácil mantener el comportamiento de tu app en todas las versiones de Android.

Cómo migrar los datos de la app

Cuando tu app esté lista para la migración, usa el siguiente enfoque:

  1. Orienta tu app a Android 10 o versiones anteriores.
  2. Inhabilita el almacenamiento específico para que tu app tenga acceso a los archivos que debes migrar.
  3. Implementa un código que use la API File para transferir archivos de su ubicación actual en /sdcard/ a una ubicación a la que se pueda acceder con almacenamiento específico:

    1. Transfiere los archivos de app privada al directorio que muestra el método getExternalFilesDir().
    2. Transfiere cualquier archivo no multimedia compartido a un subdirectorio dedicado de la app del directorio Downloads/.
  4. Quita los directorios de almacenamiento heredado de tu app del directorio /sdcard/.

Una vez que los usuarios instalan la versión nueva de tu app, completan el proceso de migración de datos en sus dispositivos. Puedes supervisar el proceso de migración en toda la base de usuarios mediante la creación de un evento de estadísticas.

Una vez que los usuarios migren sus datos, publica otra actualización en la app, orientada a Android 11.

Cómo compartir contenido con otras apps

Para compartir los archivos de tu app con otra app, usa un FileProvider. En el caso de las apps que necesitan compartir archivos entre sí, recomendamos que uses un proveedor de contenido para cada app y, luego, que sincronices los datos a medida que se agregan a la colección.

Cómo almacenar en caché archivos que no son multimedia

El enfoque que debes usar depende del tipo de archivos que necesitas almacenar en caché.

Cómo exportar archivos que no son multimedia a un dispositivo

Define una ubicación predeterminada adecuada para almacenar archivos que no son multimedia. Permite que los usuarios exporten archivos de directorios específicos de la app a una ubicación más accesible. Usa las colecciones de documentos o descargas de MediaStore para exportar archivos que no sean multimedia al dispositivo.

Cómo inhabilitar temporalmente el almacenamiento específico

Antes de que tu app sea totalmente compatible con el almacenamiento específico, puedes inhabilitarla de forma temporal en tus pruebas y en tu app de producción.

Cómo dejar de participar en tus pruebas

En Android 10 (nivel de API 29) y versiones posteriores, las pruebas de tu app se ejecutan en una zona de pruebas de almacenamiento de forma predeterminada. Esta zona de pruebas evita que la app acceda a archivos fuera del directorio específico de la app y de directorios compartidos de forma pública.

Si una prueba genera archivos para el host, como capturas de pantalla, datos de depuración, datos de cobertura o métricas de rendimiento, puedes escribir estos archivos en directorios globales. Para ello, agrega la siguiente marca al agente relevante que invoca am instrument:

-e no-isolated-storage 1

Esta marca afecta todo el comportamiento del caso de prueba de instrumentación y todo el código de prueba invocado. Por lo tanto, cuando usas esta marca, no puedes validar la compatibilidad de tu app con el almacenamiento específico. Para el resultado de la prueba, es conveniente escribir en el almacenamiento específico de la app que la shell pueda leer. Luego, puedes extraer ese directorio con almacenamiento específico. Para determinar el directorio desde el que se debe extraer, llama a getExternalMediaDirs().

Cómo inhabilitar tu app de producción

Si la app se orienta a Android 10 (nivel de API 29) o versiones anteriores, puedes inhabilitar temporalmente el almacenamiento específico de la app de producción. Sin embargo, si orientas tu app a Android 10, debes configurar el valor de requestLegacyExternalStorage como true en el archivo de manifiesto de tu app:

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting
       Android 10. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

Si deseas probar el comportamiento de una app que se orienta a Android 10 o versiones anteriores cuando usas almacenamiento específico, puedes establecer el valor de requestLegacyExternalStorage en false. Si realizas pruebas en un dispositivo con Android 11, también puedes usar marcas de compatibilidad de apps para probar el comportamiento de tu app con o sin almacenamiento específico.

Recursos adicionales

Para obtener más información sobre el almacenamiento de Android, consulta los siguientes materiales:

Entradas de blog