Guía de adaptación de permisos de almacenamiento de Android para Android 11+

Resumir con IA

Cuando intenté implementar un requisito muy simple — Descarga una imagen y guárdala en el almacenamiento local. — Al principio todo parecía estar bien.

  • Honor (Android 10) – funciona
  • Redmi (Android 11) – funciona
  • Xiaomi (Android 13) – funciona
  • Samsung (Android 13) – Falló por completo: el cuadro de diálogo de permisos de almacenamiento nunca aparecía.

Mismo código, misma función, pero un dispositivo en Android 13 simplemente se negó a mostrar la solicitud de permiso. Así es como esta pequeña tarea de "descargar imagen" se convirtió en una inmersión profunda en Almacenamiento con alcance y GESTIONAR_ALMACENAMIENTO_EXTERNO.

Esta publicación resume cómo adapté los permisos de almacenamiento para Android 11 y versiones posteriores, y cómo manejo los diferentes comportamientos en las distintas versiones.


1. El error: El diálogo de permisos de almacenamiento nunca aparece (Samsung Android 13)

El requisito es sencillo:

Descarga una imagen y guárdala en el dispositivo para que aparezca en la galería.

En mis tres dispositivos de prueba:

  • Honor – Android 10 → OK
  • Redmi – Android 11 → OK
  • Xiaomi – Android 13 → OK

Pero en un Dispositivo Samsung con Android 13, el sistema Nunca se mostró el cuadro de diálogo de permisos de almacenamiento., sin importar cómo lo solicitara.

Al principio pensé que se trataba simplemente de otra peculiaridad del fabricante, pero después de comprobar los cambios en los permisos de almacenamiento en las distintas versiones de Android, me di cuenta de que estaba utilizando un comportamiento que, en la práctica, había quedado obsoleto en Android 13 al usar el SDK 33.


2. Causa raíz: Las funciones WRITE/READ_EXTERNAL_STORAGE están obsoletas en Android 13 (SDK 33).

En versiones anteriores de Android, podíamos simplemente declarar estos dos permisos en el manifiesto:

  • LECTURA_ALMACENAMIENTO_EXTERNO
  • ESCRIBIR_ALMACENAMIENTO_EXTERNO

y luego solicitarlas en tiempo de ejecución cuando sea necesario.

En Android 13 (SDK 33) con targetSdkVersion = 33, Ese enfoque empieza a fallar:

  • ESCRIBIR_ALMACENAMIENTO_EXTERNO es obsoleto y prácticamente inútil en versiones recientes de Android
  • Si añades maxSdkVersion=32 Estos permisos siguen funcionando en Android 11/12.
    pero lo son ignorado en Android 13 al apuntar a 33
  • Al mismo tiempo, Play Store exige que las nuevas aplicaciones tengan como objetivo al menos el SDK 33.

Por lo tanto, para Android 11+ debemos adaptarnos a:

  • Almacenamiento con alcance
  • Y en algunos casos, el permiso especial: GESTIONAR_ALMACENAMIENTO_EXTERNO

GESTIONAR_ALMACENAMIENTO_EXTERNO otorga a una aplicación un amplio acceso a todo el contenido del almacenamiento compartido (incluidos los archivos que no son multimedia). no Permite el acceso a los directorios privados de otras aplicaciones, pero Google Play aún lo considera un permiso altamente sensible.

Para dar soporte a diferentes versiones de Android, terminé dividiendo el manejo de permisos en:

  • Antes de Android 11 (API < 30) – Permisos de almacenamiento externo de estilo antiguo
  • Android 11 y versiones posteriores – Almacenamiento delimitado + manipulación especial con GESTIONAR_ALMACENAMIENTO_EXTERNO donde sea estrictamente necesario

3. Adaptación paso a paso

3.1 Declarar MANAGE_EXTERNAL_STORAGE en el manifiesto

En el AndroidManifest.xml:

<uses-permission
    android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    tools:ignore="ScopedStorage" />

⚠️ Nota: Porque GESTIONAR_ALMACENAMIENTO_EXTERNO Es un permiso delicado, está restringido en Google Play. Hablaré de alternativas más adelante si solo quieres guardar imágenes.


3.2 Comprobar si se ha concedido el permiso

Yo usé Permisos fáciles para simplificar las comprobaciones de permisos.

private fun checkPer(activity: PreViewActivity): Boolean { return if (Build.VERSION.SDK_INT >= 30) { EasyPermissions.hasPermissions( activity, android.Manifest.permission.MANAGE_EXTERNAL_STORAGE ) } else { EasyPermissions.hasPermissions( activity, android.Manifest.permission.WRITE_EXTERNAL_STORAGE ) } }
  • En Android 11+ (API >= 30): Yo reviso GESTIONAR_ALMACENAMIENTO_EXTERNO
  • En Android 10 y versiones anteriores: Todavía reviso ESCRIBIR_ALMACENAMIENTO_EXTERNO

Esta división es crucial, porque ESCRIBIR_ALMACENAMIENTO_EXTERNO Ya no se comporta como lo hacía en las versiones más recientes.


3.3 Solicitar permiso cuando no esté disponible

Si no se concede el permiso, lo solicito de forma diferente según la versión del sistema.

private fun requestStoragePermission(activity: PreViewActivity, curImg: Int) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+ – redirigir a la página de configuración del sistema "Acceso a todos los archivos" val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:" + activity.packageName) activity.startActivityForResult(intent, 200) } else { // Android 10 y versiones anteriores – permiso de tiempo de ejecución normal val perm = android.Manifest.permission.WRITE_EXTERNAL_STORAGE PaperThreeVariable.isToRequestPer = true EasyPermissions.requestPermissions( PermissionRequest.Builder( activity, 200, perm ).build() ) } }
  • En Android 11+: no puedes simplemente “abrir” un diálogo de tiempo de ejecución normal para GESTIONAR_ALMACENAMIENTO_EXTERNO
    Debe enviar al usuario a la página de configuración del sistema donde deberá otorgar manualmente el "Acceso a todos los archivos".
  • En Android 10 y versiones anteriores: El diálogo clásico de permisos en tiempo de ejecución sigue funcionando.

3.4 Manejo de devoluciones de llamada de permisos

EasyPermissions ayuda a conectar la función de devolución de llamada de la actividad con nuestra propia lógica:

override fun onRequestPermissionsResult( requestCode: Int, permissions: Array , grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) } override fun onPermissionsGranted(requestCode: Int, perms: MutableList ) { AppInitUtils().saveFreshAppImageToGallery(this, curImg) PaperThreeVariable.isToRequestPer = false } override fun onPermissionsDenied(requestCode: Int, perms: MutableList ) { PaperThreeVariable.isToRequestPer = false if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { AppSettingsDialog.Builder(this) .setRationale("Esta función requiere que se habilite el permiso de almacenamiento") .setNegativeButton("No") .setPositiveButton("Sí") .build() .show() } }

Por qué utilizo EasyPermissions aquí:

  • Los usuarios pueden negar permanentemente permisos, lo que provoca que las solicitudes repetidas fallen silenciosamente
  • EasyPermissions facilita lo siguiente:
    • Detectar estado de “denegación permanente”
    • Mostrar un cuadro de diálogo que guíe a los usuarios a Configuración del sistema → Permisos de la aplicación para habilitar el acceso al almacenamiento manualmente

Una vez concedido el permiso, llamo:

AppInitUtils().saveFreshAppImageToGallery(this, curImg)

para guardar la imagen y actualizar la galería.

Tras esta adaptación, el dispositivo Samsung con Android 13 finalmente se comportó igual que los demás.

Nota al margen: Mi dispositivo Xiaomi se identificaba como Android 13, pero la sección "Dispositivos conectados históricos" de Android Studio lo reconocía como Android 12. Esto podría explicar por qué seguía funcionando en algunos casos, pero precisamente por eso es importante gestionar los permisos teniendo en cuenta la versión del sistema.


4. Acerca de MANAGE_EXTERNAL_STORAGE y las restricciones de Google Play

GESTIONAR_ALMACENAMIENTO_EXTERNO es poderoso:

Otorga acceso de lectura/escritura a todo el almacenamiento compartido en el dispositivo.

Debido a esto, Google Play lo trata como un permiso altamente confidencial:

  • Está destinado principalmente a administrador de archivos / copia de seguridad / antivirus aplicaciones de tipo
  • Debes presentar una justificación para usarlo.
  • Si tu aplicación es solo una aplicación de consumo típica (por ejemplo, guardar imágenes, descargas simples), lo más probable es que tu solicitud sea rechazado

Entonces, si su único requisito es:

“Guarda una imagen en la galería y hazla visible para el usuario.”

entonces deberías Considere seriamente evitar GESTIONAR_ALMACENAMIENTO_EXTERNO y en su lugar:

  • Usar MediaStore para insertar imágenes en la biblioteca de medios del sistema
  • O bien, utilice API que puedan guardar imágenes sin necesidad de acceder al archivo completo.

Existen varios patrones para:

  • Guarda una imagen en el directorio Pictures/DCIM.
  • Notifica al escáner de medios o confía en MediaStore para que la galería pueda detectarlo.
  • Haz todo esto sin solicitar MANAGE_EXTERNAL_STORAGE

Para la distribución interna o fuera de Play Store (por ejemplo, tiendas de aplicaciones internas empresariales), técnicamente aún puede usar Entorno.getExternalStorageDirectory(), pero no recomiendo diseñar una nueva aplicación basada en esto en 2025.


5. Resumen versión por versión (Android 9 → 13)

Para tenerlo todo claro, aquí tenéis un resumen general de cómo se comportan el almacenamiento externo y los permisos en las distintas versiones.

Android 9 y versiones anteriores (API 28 y anteriores)

  • Permisos:
    • LECTURA_ALMACENAMIENTO_EXTERNO
    • ESCRIBIR_ALMACENAMIENTO_EXTERNO
  • Comportamiento:
    • Las aplicaciones pueden acceder libremente /tarjeta sd y sus subdirectorios
    • Los archivos creados por la aplicación permanecen en el dispositivo incluso después de desinstalarla.
  • Enfoque típico:
    • Lectura/escritura directa en rutas de almacenamiento externas.

Android 10 (API 29) – Introducción del almacenamiento con ámbito

  • Permisos:
    • LECTURA_ALMACENAMIENTO_EXTERNO Todavía funciona
    • ESCRIBIR_ALMACENAMIENTO_EXTERNO Todavía existe, pero su alcance efectivo se ha reducido.
  • Comportamiento:
    • Almacenamiento con alcance se presenta:
      • Las aplicaciones están limitadas a su propio directorio específico de aplicaciones en
        Android/datos/nombre.de.tu.paquete/
      • El acceso directo a los archivos de otras aplicaciones está restringido.
    • Se debe acceder a los archivos multimedia (imágenes, vídeos, audio) a través de MediaStore
    • requestLegacyExternalStorage=true podría mantener temporalmente el comportamiento anterior
      (pero esta bandera se ignora a partir de Android 11)
  • Enfoque recomendado:
    • Para imágenes/vídeos/audio: utilice MediaStore
    • Para archivos privados: utilice obtenerDirectorioDeArchivosExternos() o obtenerDirectorioDeDatos()

Android 11 (API 30) – Almacenamiento con ámbito restringido (aplicado)

  • Permisos:
    • LECTURA_ALMACENAMIENTO_EXTERNO Funciona, pero solo para contenido multimedia gestionado por MediaStore.
    • ESCRIBIR_ALMACENAMIENTO_EXTERNO es efectivamente obsoleto para almacenamiento externo general
    • GESTIONAR_ALMACENAMIENTO_EXTERNO Introducido para casos de uso especiales de “acceso a todos los archivos”.
  • Comportamiento:
    • requestLegacyExternalStorage=true ya no funciona; Almacenamiento con ámbito es siempre encendido
    • Acceso a /tarjetas SD/ La raíz está bloqueada
    • Las aplicaciones solo pueden:
      • Acceda a sus propios directorios privados
      • Acceda a los medios compartidos a través de MediaStore
  • Enfoque recomendado:
    • Para aplicaciones típicas:
      • Utilice MediaStore o Fuerzas Armadas de Singapur (ACCIÓN_ABRIR_DOCUMENTO, ACCIÓN_CREAR_DOCUMENTO) para archivos seleccionados por el usuario
    • Solo considere GESTIONAR_ALMACENAMIENTO_EXTERNO si tu aplicación es realmente un administrador de archivos, una herramienta de copia de seguridad, una aplicación de seguridad, etc.

Android 13 (API 33) – División de permisos multimedia

  • Permisos:
    • LEER_IMÁGENES_MEDIA – acceder a las imágenes
    • LEER_VIDEO_MEDIA – acceder a los vídeos
    • LEER_MEDIA_AUDIO – acceso al audio
  • Comportamiento:
    • Los permisos de los medios son de grano fino:
      • Los usuarios pueden otorgar acceso solo a imágenes, solo a vídeos, etc.
    • Las reglas de almacenamiento con ámbito de Android 11 siguen vigentes.
  • Enfoque recomendado:
    • Solicita los permisos de medios específicos que necesitas:
      • Por ejemplo, si solo trabaja con imágenes, solicite solo LEER_IMÁGENES_MEDIA
    • Hacer no pedido LECTURA_ALMACENAMIENTO_EXTERNO en Android 13+, ya que se reemplaza por los nuevos permisos de medios

Matriz rápida (conceptual)

  • Android 9 y versiones anteriores
    • El acceso al almacenamiento externo es amplio y está controlado por READ/WRITE_EXTERNAL_STORAGE.
  • Android 10
    • Se ha introducido el almacenamiento con ámbito, pero existen vías de escape (solicitudAlmacenamientoExternoLegado)
  • Android 11
    • Se aplica el almacenamiento con ámbito restringido y se eliminan los conmutadores heredados.
    • GESTIONAR_ALMACENAMIENTO_EXTERNO aparece, pero está muy restringido.
  • Android 13
    • El acceso a los medios se dividió en LEER_MEDIOS_* permisos
    • Las mismas reglas de almacenamiento con ámbito definido, pero con un control de usuario más preciso.

6. Conclusiones

  • No des por sentado que "funciona en un dispositivo Android 13" significa que funciona en todas partes; los fabricantes de equipos originales y los informes del sistema pueden ser inconsistentes.
  • Para Android 11+, Piensa en términos de:
    • Directorios privados de la aplicación + MediaStore + SAF, no “crudo” /tarjeta sd acceso"”
  • Tratar GESTIONAR_ALMACENAMIENTO_EXTERNO como un último recurso Para tipos de aplicaciones muy específicos, especialmente si planeas publicarlas en Google Play.
  • Siempre prueba en Múltiples dispositivos y versiones de Android, especialmente en lo que respecta a permisos y almacenamiento.

Este artículo surge de mi propia experiencia depurando y adaptando el texto en proyectos reales de Android (incluido un dispositivo Samsung con Android 13). GPT solo colaboró en la traducción y el perfeccionamiento de la redacción; todo el contenido técnico y las decisiones son responsabilidad mía.

Acerca del autor

Compartir publicación:

Mantente conectado

Más actualizaciones