Resumen
Puede otorgar acceso de lectura / escritura a la tarjeta SD externa en los diferentes niveles de API ( API23 + en tiempo de ejecución ).
Desde KitKat, los permisos no son necesarios si usa directorios específicos de la aplicación; de lo contrario , se requieren .
Manera universal:
La historia dice que no hay una forma universal de escribir en una tarjeta SD externa, pero continúa ...
Este hecho lo demuestran estos ejemplos de configuraciones de almacenamiento externo para dispositivos .
Manera basada en API:
Antes de KitKat, intente utilizar el método 1 de Doomsknight, de lo contrario el método 2.
Solicite permisos en el manifiesto (Api <23) y en tiempo de ejecución (Api> = 23).
Forma recomendada:
ContextCompat.getExternalFilesDirs resuelve el error de acceso cuando no necesita compartir archivos.
La forma segura de compartirlo es utilizar un proveedor de contenido o el nuevo Storage Access Framework .
Manera consciente de la privacidad:
A partir de Android Q Beta 4 , las aplicaciones destinadas a Android 9 (nivel de API 28) o versiones anteriores no ven ningún cambio, de forma predeterminada.
Las aplicaciones destinadas a Android Q de forma predeterminada (o que lo habilitan) reciben una vista filtrada en el almacenamiento externo.
1. Respuesta inicial.
Manera universal de escribir en una tarjeta SD externa en Android
No existe una forma universal de escribir en una tarjeta SD externa en Android debido a los cambios continuos :
Pre-KitKat: la plataforma oficial de Android no ha admitido tarjetas SD, excepto por excepciones.
KitKat: introdujo API que permiten que las aplicaciones accedan a archivos en directorios específicos de aplicaciones en tarjetas SD.
Lollipop: API agregadas para permitir que las aplicaciones soliciten acceso a carpetas que pertenecen a otros proveedores.
Nougat: proporcionó una API simplificada para acceder a directorios de almacenamiento externo comunes.
... Cambio de privacidad de Android Q: almacenamiento de ámbito de aplicación y de ámbito de medios
¿Cuál es la mejor manera de otorgar acceso de lectura / escritura a la tarjeta SD externa en diferentes niveles de API?
Basado en la respuesta de Doomsknight y la mía , y en las publicaciones del blog de Dave Smith y Mark Murphy: 1 , 2 , 3 :
2. Respuesta actualizada.
Actualización 1 . Probé el Método 1 de la respuesta de Doomknight, sin resultado:
Como puede ver, estoy verificando los permisos en tiempo de ejecución antes de intentar escribir en SD ...
Me gustaría utilizar directorios específicos de la aplicación para evitar el problema de su pregunta y actualizada ContextCompat.getExternalFilesDirs()
utilizando la documentación getExternalFilesDir como referencia.
Mejore la heurística para determinar qué representa los medios extraíbles en función de los diferentes niveles de API comoandroid.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT
... Pero me sale un error de acceso, probé en dos dispositivos diferentes: HTC10 y Shield K1.
Recuerde que Android 6.0 admite dispositivos de almacenamiento portátiles y las aplicaciones de terceros deben pasar por Storage Access Framework . Sus dispositivos HTC10 y Shield K1 probablemente sean API 23.
Su registro muestra un permiso denegado de acceso de excepción/mnt/media_rw
, como esta solución para API 19+:
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_r" />
<group gid="sdcard_rw" />
<group gid="media_rw" />
</permission>
Nunca lo probé, así que no puedo compartir código, pero evitaría for
intentar escribir en todos los directorios devueltos y buscaría el mejor directorio de almacenamiento disponible para escribir en función del espacio restante .
Quizás la alternativa de Gizm0 a su getStorageDirectories()
método sea un buen punto de partida.
ContextCompat.getExternalFilesDirs
resuelve el problema si no necesita acceder a otras carpetas .
3. Android 1.0 .. Pre-KitKat.
Antes de KitKat, intente usar el método 1 de Doomsknight o lea esta respuesta de Gnathonic.
public static HashSet<String> getExternalMounts() {
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try {
final Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
s = s + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
final String[] lines = s.split("\n");
for (String line : lines) {
if (!line.toLowerCase(Locale.US).contains("asec")) {
if (line.matches(reg)) {
String[] parts = line.split(" ");
for (String part : parts) {
if (part.startsWith("/"))
if (!part.toLowerCase(Locale.US).contains("vold"))
out.add(part);
}
}
}
}
return out;
}
Agregue el siguiente código a su AndroidManifest.xml
y lea Cómo obtener acceso al almacenamiento externo
El acceso al almacenamiento externo está protegido por varios permisos de Android.
A partir de Android 1.0, el acceso de escritura está protegido con el WRITE_EXTERNAL_STORAGE
permiso .
A partir de Android 4.1, el acceso de lectura está protegido con el READ_EXTERNAL_STORAGE
permiso.
Para ... escribir archivos en el almacenamiento externo, su aplicación debe adquirir ... permisos del sistema:
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
Si necesita ambos ..., solo debe solicitar el WRITE_EXTERNAL_STORAGE
permiso.
Leer la explicación de la marca Murphy y recomendados Dianne Hackborn y Dave Smith mensajes
- Hasta Android 4.4, no había soporte oficial para medios extraíbles en Android. A partir de KitKat, el concepto de almacenamiento externo "primario" y "secundario" surge en la API de FMW.
- Las aplicaciones anteriores solo se basan en la indexación de MediaStore, se envían con el hardware o examinan los puntos de montaje y aplican algunas heurísticas para determinar qué representa los medios extraíbles.
Ignore la siguiente nota debido a errores, pero intente usar ContextCompat.getExternalFilesDirs()
:
- Desde Android 4.2, ha habido una solicitud de Google para que los fabricantes de dispositivos bloqueen los medios extraíbles por seguridad (soporte para múltiples usuarios) y se agregaron nuevas pruebas en 4.4.
- Dado
getExternalFilesDirs()
que se agregaron KitKat y otros métodos para devolver una ruta utilizable en todos los volúmenes de almacenamiento disponibles (el primer elemento devuelto es el volumen principal).
- La siguiente tabla indica lo que un desarrollador podría intentar hacer y cómo responderá KitKat:
Nota: A partir de Android 4.4, estos permisos no son necesarios si está leyendo o escribiendo solo archivos que son privados para su aplicación. Para obtener más información ..., consulte guardar archivos privados de la aplicación .
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
</manifest>
Lea también la explicación de Paolo Rovelli e intente usar la solución de Jeff Sharkey desde KitKat:
En KitKat ahora hay una API pública para interactuar con estos dispositivos secundarios de almacenamiento compartido.
Los métodos nuevos Context.getExternalFilesDirs()
y
Context.getExternalCacheDirs()
pueden devolver varias rutas, incluidos los dispositivos primarios y secundarios.
Luego, puede iterar sobre ellos y verificar Environment.getStorageState()
y
File.getFreeSpace()
determinar el mejor lugar para almacenar sus archivos.
Estos métodos también están disponibles ContextCompat
en la biblioteca support-v4.
A partir de Android 4.4, el propietario, el grupo y los modos de los archivos en los dispositivos de almacenamiento externos ahora se sintetizan en función de la estructura de directorios. Esto permite que las aplicaciones administren sus directorios específicos de paquetes en un almacenamiento externo sin requerir que tengan el WRITE_EXTERNAL_STORAGE
permiso amplio
. Por ejemplo, la aplicación con el nombre del paquete com.example.foo
ahora puede acceder libremente
Android/data/com.example.foo/
en dispositivos de almacenamiento externos sin permisos. Estos permisos sintetizados se logran al empaquetar los dispositivos de almacenamiento sin procesar en un demonio FUSE.
Con KitKat, sus posibilidades de obtener una "solución completa" sin rootear son prácticamente nulas:
El proyecto de Android definitivamente se ha equivocado aquí. Ninguna aplicación tiene acceso completo a tarjetas SD externas:
- administradores de archivos: no puede usarlos para administrar su tarjeta SD externa. En la mayoría de las áreas, solo pueden leer pero no escribir.
- Aplicaciones multimedia: ya no puede volver a etiquetar / reorganizar su colección multimedia, ya que esas aplicaciones no pueden escribir en ella.
- aplicaciones de oficina: prácticamente lo mismo
El único sitio 3 ª Parte aplicaciones pueden escribir en la tarjeta externa son "sus propios directorios" (es decir
/sdcard/Android/data/<package_name_of_the_app>
).
Las únicas formas de solucionarlo realmente requieren del fabricante (algunos de ellos lo arreglaron, por ejemplo, Huawei con su actualización de Kitkat para el P6) - o root ... (la explicación de Izzy continúa aquí)
5. Android 5.0 introdujo cambios y la clase auxiliar DocumentFile .
getStorageState
Agregado en API 19, obsoleto en API 21,
usegetExternalStorageState(File)
Aquí hay un excelente tutorial para interactuar con Storage Access Framework en KitKat.
Interactuar con las nuevas API en Lollipop es muy similar (explicación de Jeff Sharkey) .
Solicite permisos en tiempo de ejecución si API nivel 23+ y lea Solicitud de permisos en tiempo de ejecución
A partir de Android 6.0 (nivel de API 23), los usuarios otorgan permisos a las aplicaciones mientras la aplicación se está ejecutando, no cuando instalan la aplicación ... o actualizan la aplicación ... el usuario puede revocar los permisos.
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
Android 6.0 presenta un nuevo modelo de permisos en tiempo de ejecución donde las aplicaciones solicitan capacidades cuando las necesitan en tiempo de ejecución. Debido a que el nuevo modelo incluye los READ/WRITE_EXTERNAL_STORAGE
permisos, la plataforma debe otorgar acceso al almacenamiento de forma dinámica sin matar ni reiniciar las aplicaciones que ya se están ejecutando. Lo hace manteniendo tres vistas distintas de todos los dispositivos de almacenamiento montados:
- / mnt / runtime / default se muestra en aplicaciones sin permisos especiales de almacenamiento ...
- / mnt / runtime / read se muestra a las aplicaciones con READ_EXTERNAL_STORAGE
- / mnt / runtime / write se muestra en aplicaciones con WRITE_EXTERNAL_STORAGE
7. Android 7.0 proporciona una API simplificada para acceder a directorios de almacenamiento externo.
Acceso a directorios con alcance
En Android 7.0, las aplicaciones pueden usar nuevas API para solicitar acceso a directorios de almacenamiento externo específicos
, incluidos directorios en medios extraíbles como tarjetas SD ...
Para obtener más información, consulte la capacitación de Acceso a directorios con ámbito .
Lea las publicaciones de Mark Murphy: Tenga cuidado con el acceso al directorio con alcance . Quedó obsoleto en Android Q :
Tenga en cuenta que el acceso al directorio con ámbito agregado en 7.0 está obsoleto en Android Q.
Específicamente, el createAccessIntent()
método en StorageVolume está en desuso.
Agregaron una createOpenDocumentTreeIntent()
que se puede utilizar como alternativa.
8. Android 8.0 Oreo .. Cambios en Android Q Beta.
A partir de Android O , Storage Access Framework permite a los proveedores de documentos personalizados
crear descriptores de archivos buscables para archivos que residen en una fuente de datos remota ...
Permisos ,
antes de Android O , si una aplicación solicitaba un permiso en tiempo de ejecución y se concedía el permiso, el sistema también concedía incorrectamente a la aplicación el resto de los permisos que pertenecían al mismo grupo de permisos y que estaban registrados en el manifiesto.
Para las aplicaciones destinadas a Android O , este comportamiento se ha corregido. La aplicación solo recibe los permisos que solicitó explícitamente. Sin embargo, una vez que el usuario otorga un permiso a la aplicación, todas las solicitudes posteriores de permisos en ese grupo de permisos se otorgan automáticamente.
Por ejemplo, READ_EXTERNAL_STORAGE
y WRITE_EXTERNAL_STORAGE
...
Actualización: una versión beta anterior de Android Q reemplazó temporalmente los permisos READ_EXTERNAL_STORAGE
y WRITE_EXTERNAL_STORAGE
con permisos más detallados y específicos para los medios.
Nota: Google introdujo roles en Beta 1 y los eliminó de la documentación antes de Beta 2 ...
Nota: los permisos específicos a las colecciones de los medios que se introdujeron en releases- anterior beta READ_MEDIA_IMAGES
, READ_MEDIA_AUDIO
y READ_MEDIA_VIDEO
- se han quedado obsoletas . Más información:
Revisión de Q Beta 4 (API finales) de Mark Murphy: La muerte del almacenamiento externo: El fin de la saga (?)
"La muerte es más universal que la vida. Todos mueren, pero no todos viven". - Andrew Sachs
9. Preguntas relacionadas y respuestas recomendadas.
¿Cómo puedo obtener la ruta de la tarjeta SD externa para Android 4.0+?
mkdir () funciona dentro del almacenamiento flash interno, pero no en la tarjeta SD?
Diferencia entre getExternalFilesDir y getExternalStorageDirectory ()
¿Por qué getExternalFilesDirs () no funciona en algunos dispositivos?
Cómo utilizar la nueva API de acceso a la tarjeta SD presentada para Android 5.0 (Lollipop)
Escribir en una tarjeta SD externa en Android 5.0 y superior
Permiso de escritura de la tarjeta SD de Android usando SAF (Storage Access Framework)
SAFFAQ: Preguntas frecuentes sobre el marco de acceso al almacenamiento
10. Errores y problemas relacionados.
Error: en Android 6, al usar getExternalFilesDirs, no le permitirá crear nuevos archivos en sus resultados
La escritura en el directorio devuelto por getExternalCacheDir () en Lollipop falla sin permiso de escritura