tarjeta studio ruta raiz micro memoria manejo los guardan ficheros externa donde directorio cual crear archivos archivo abrir java android android-sdcard android-externalstorage android-external-storage

java - ruta - manejo de archivos en android studio



Forma universal de escribir en una tarjeta SD externa en Android (7)

En mi aplicación, necesito almacenar muchas imágenes en el almacenamiento del dispositivo. Dichos archivos tienden a cumplir con el almacenamiento del dispositivo, y quiero permitir que los usuarios puedan elegir una tarjeta SD externa como carpeta de destino.

Leí en todas partes que Android no permite a los usuarios escribir en una tarjeta SD externa, me refiero a la tarjeta SD externa y montable y no al almacenamiento externo , pero las aplicaciones del administrador de archivos logran escribir en la SD externa en todas las versiones de Android.

¿Cuál es la mejor manera de otorgar acceso de lectura / escritura a la tarjeta SD externa en diferentes niveles de API (Pre-KitKat, KitKat, Lollipop +)?

Actualización 1

Intenté el Método 1 de la respuesta de Doomknight, sin resultado: Como puede ver, estoy buscando permisos en tiempo de ejecución antes de intentar escribir en SD:

HashSet<String> extDirs = getStorageDirectories(); for(String dir: extDirs) { Log.e("SD",dir); File f = new File(new File(dir),"TEST.TXT"); try { if(ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED) { f.createNewFile(); } } catch (IOException e) { e.printStackTrace(); } }

Pero recibo un error de acceso, probé en dos dispositivos diferentes: HTC10 y Shield K1.

10-22 14:52:57.329 30280-30280/? E/SD: /mnt/media_rw/F38E-14F8 10-22 14:52:57.329 30280-30280/? W/System.err: java.io.IOException: open failed: EACCES (Permission denied) 10-22 14:52:57.329 30280-30280/? W/System.err: at java.io.File.createNewFile(File.java:939) 10-22 14:52:57.329 30280-30280/? W/System.err: at com.myapp.activities.TestActivity.onResume(TestActivity.java:167) 10-22 14:52:57.329 30280-30280/? W/System.err: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1326) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.Activity.performResume(Activity.java:6338) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3336) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2574) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.access$900(ActivityThread.java:150) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1399) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.os.Looper.loop(Looper.java:168) 10-22 14:52:57.330 30280-30280/? W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5885) 10-22 14:52:57.330 30280-30280/? W/System.err: at java.lang.reflect.Method.invoke(Native Method) 10-22 14:52:57.330 30280-30280/? W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:819) 10-22 14:52:57.330 30280-30280/? W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:709) 10-22 14:52:57.330 30280-30280/? W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied) 10-22 14:52:57.330 30280-30280/? W/System.err: at libcore.io.Posix.open(Native Method) 10-22 14:52:57.330 30280-30280/? W/System.err: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186) 10-22 14:52:57.330 30280-30280/? W/System.err: at java.io.File.createNewFile(File.java:932) 10-22 14:52:57.330 30280-30280/? W/System.err: ... 14 more


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 utiliza directorios específicos de la aplicación, de required contrario se required .

Manera universal:

La history dice que no hay una forma universal de escribir en una tarjeta SD externa, pero continúa ...

Este hecho queda demostrado por estos ejemplos de configuraciones de almacenamiento externo para dispositivos .

Forma basada en API:

Antes de KitKat intente utilizar Doomsknight método 1, método 2 de lo contrario.

Solicite permisos en 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 usar un proveedor de contenido o el nuevo Marco de acceso de almacenamiento .

Forma de privacidad:

A partir de Android Q Beta 4 , las aplicaciones que se dirigen a Android 9 (nivel de API 28) o inferior no ven cambios, de forma predeterminada.

Las aplicaciones que se dirigen a Android Q de forma predeterminada (u optan por él) reciben una vista filtrada en el almacenamiento externo.

1. Respuesta inicial.

Forma universal de escribir en una tarjeta SD externa en Android

No hay una forma universal de escribir en una tarjeta SD externa en Android debido a cambios continuos :

  • Pre-KitKat: la plataforma oficial de Android no es compatible con las tarjetas SD, salvo excepciones.

  • KitKat: API introducidas 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 de otros proveedores.

  • Turrón: proporcionó una API simplificada para acceder a directorios comunes de almacenamiento externo.

  • ... Cambio de privacidad de Android Q: almacenamiento con ámbito de aplicación y á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 mine , y en las publicaciones de blog de Dave Smith y Mark Murphy: 1 , 2 , 3 :

2. Respuesta actualizada.

Actualización 1 . Intenté el Método 1 de la respuesta de Doomknight, sin resultado:

Como puede ver, estoy buscando permisos en tiempo de ejecución antes de intentar escribir en SD ...

ContextCompat.getExternalFilesDirs() directorios específicos de la aplicación para evitar el problema de su pregunta actualizada y ContextCompat.getExternalFilesDirs() usando la documentación de getExternalFilesDir como referencia.

Mejore la heuristics para determinar qué representa los medios extraíbles en función de los diferentes niveles de la API como android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT

... Pero recibo 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 son probablemente API 23.

Su registro muestra una excepción de permiso denegado para acceder a /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" /> // this line is added via root in the link to fix it. </permission>

Nunca lo intenté, así que no puedo compartir el código, pero evitaría intentar escribir en todos los directorios devueltos y buscar el mejor directorio de almacenamiento disponible para escribir en función del espacio restante .

Quizás la alternativa de getStorageDirectories() a su método getStorageDirectories() es un buen punto de partida.

ContextCompat.getExternalFilesDirs resuelve el problema si no necesita acceso a otras carpetas .

3. Android 1.0 .. Pre-KitKat.

Antes de KitKat, intente utilizar el método Doomsknight 1 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(); } // parse output 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 permiso WRITE_EXTERNAL_STORAGE .

A partir de Android 4.1, el acceso de lectura está protegido con el permiso READ_EXTERNAL_STORAGE .

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 ..., debe solicitar solo el permiso WRITE_EXTERNAL_STORAGE .

Lea la explicación de Mark Murphy y recommended publicaciones de Dianne Hackborn y Dave Smith

  • 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" emerge en la API 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.

4. Android 4.4 KitKat presenta el Storage Access Framework (SAF) .

Ignore la siguiente nota debido a errores, pero intente usar ContextCompat.getExternalFilesDirs() :

  • Desde Android 4.2, Google ha solicitado a los fabricantes de dispositivos que bloqueen los medios extraíbles por seguridad (soporte multiusuario) y se agregaron nuevas pruebas en 4.4.
  • Dado que KitKat getExternalFilesDirs() y otros métodos se agregaron 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 que son 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 heuristics desde KitKat:

En KitKat ahora hay una API pública para interactuar con estos dispositivos secundarios de almacenamiento compartido.

Los nuevos Context.getExternalFilesDirs() y Context.getExternalCacheDirs() pueden devolver múltiples rutas, incluidos los dispositivos primarios y secundarios.

Luego puede iterar sobre ellos y verificar Environment.getStorageState() y File.getFreeSpace() para determinar el mejor lugar para almacenar sus archivos.

Estos métodos también están disponibles en ContextCompat.getExternalFilesDirs 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 externo ahora se sintetizan según la estructura del directorio. Esto permite que las aplicaciones administren sus directorios específicos de paquetes en almacenamiento externo sin requerir que tengan el amplio permiso WRITE_EXTERNAL_STORAGE . Por ejemplo, la aplicación con el nombre del paquete com.example.foo ahora puede acceder libremente a Android/data/com.example.foo/ en dispositivos de almacenamiento externo sin permisos. Estos permisos sintetizados se logran envolviendo dispositivos de almacenamiento sin procesar en un demonio FUSE.

Con KitKat, sus posibilidades de obtener una "solución completa" sin enraizamiento son prácticamente nulas:

El proyecto de Android definitivamente se ha arruinado 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 de medios: ya no puede volver a organizar / reorganizar su colección de medios, ya que esas aplicaciones no pueden escribir en ella.
  • aplicaciones de oficina: más o menos lo mismo

El único lugar donde las aplicaciones de terceros pueden escribir en su tarjeta externa es "sus propios directorios" (es decir, /sdcard/Android/data/<package_name_of_the_app> ).

Las únicas formas de arreglarlo realmente requieren el fabricante (algunos lo arreglaron, por ejemplo, Huawei con su actualización de Kitkat para el P6) o la raíz ... (la explicación de Izzy continúa aquí)

5. Android 5.0 introdujo cambios y la clase auxiliar DocumentFile .

getStorageState Agregado en API 19, en desuso en API 21, use getExternalStorageState(File)

Aquí hay un gran tutorial para interactuar con Storage Access Framework en KitKat.

La interacción con las nuevas API en Lollipop es muy similar (explicación de Jeff Sharkey) .

6. Android 6.0 Marshmallow presenta un nuevo modelo de permisos de tiempo de ejecución .

Solicite permisos en tiempo de ejecución si el nivel de API 23+ y lea Solicitar permisos en tiempo de ejecución

A partir de Android 6.0 (API nivel 23), los usuarios otorgan permisos a las aplicaciones mientras se ejecuta la aplicación, no cuando instalan la aplicación ... o actualizan la aplicación ... el usuario puede revocar los permisos.

// Assume thisActivity is the current activity int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

Android 6.0 presenta un nuevo modelo de permisos de tiempo de ejecución donde las aplicaciones solicitan capacidades cuando es necesario en tiempo de ejecución. Debido a que el nuevo modelo incluye los permisos READ/WRITE_EXTERNAL_STORAGE , la plataforma necesita otorgar dinámicamente acceso de almacenamiento sin matar o 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 a las aplicaciones sin permisos de almacenamiento especiales ...
  • / mnt / runtime / read se muestra a las aplicaciones con READ_EXTERNAL_STORAGE
  • / mnt / runtime / write se muestra a las aplicaciones con WRITE_EXTERNAL_STORAGE

7. Android 7.0 proporciona una API simplificada para acceder a directorios de almacenamiento externo.

Acceso al directorio 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 Acceso al directorio con ámbito .

Lea las publicaciones de Mark Murphy: Tenga cuidado con el acceso al directorio con alcance . Fue desaprobado en Android Q :

Tenga en cuenta que el acceso al directorio de ámbito agregado en 7.0 está en desuso en Android Q.

Específicamente, el método createAccessIntent() en StorageVolume está en desuso.

createOpenDocumentTreeIntent() un createOpenDocumentTreeIntent() que se puede usar como alternativa.

8. Android 8.0 Oreo .. Android Q Beta cambia.

A partir de Android O , Storage Access Framework permite a los proveedores de documentos personalizados crear descriptores de archivo buscables para archivos que residen en una fuente de datos remota ...

Permisos , antes de Android O , si una aplicación solicitó un permiso en tiempo de ejecución y se le otorgó el permiso, el sistema también le otorgó incorrectamente a la aplicación el resto de los permisos que pertenecían al mismo grupo de permisos y que se registraron en el manifiesto.

Para las aplicaciones dirigidas a Android O , este comportamiento se ha corregido. La aplicación solo recibe los permisos que ha solicitado explícitamente. Sin embargo, una vez que el usuario concede 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 READ_EXTERNAL_STORAGE y WRITE_EXTERNAL_STORAGE con más permisos específicos de medios específicos.

Nota: Google introdujo roles en Beta 1 y los eliminó de la documentación antes de Beta 2 ...

Nota: Los permisos específicos para las colecciones de medios que se introdujeron en versiones beta anteriores ( READ_MEDIA_IMAGES , READ_MEDIA_AUDIO y READ_MEDIA_VIDEO ) ahora están obsoletos . Más información:

Revisión de Q Beta 4 (API finales) por Mark Murphy: The Death of External Storage: The End of the 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 usar 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 con SAF (Storage Access Framework)

SAFFAQ: Preguntas frecuentes sobre el marco de acceso al almacenamiento

10. Errores y problemas relacionados.

Error: en Android 6, cuando usa 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


Aquí hay una forma de crear un nuevo archivo en el almacenamiento externo (SDCard si está presente en el dispositivo o en el almacenamiento externo del dispositivo si no). Simplemente reemplace "nombre de carpeta" con el nombre de la carpeta de destino deseada y "nombre de archivo" con el nombre del archivo que está guardando. Por supuesto, aquí puede ver cómo guardar un archivo genérico, ahora puede buscar cómo guardar imágenes, tal vez aquí o lo que sea en un archivo.

try { File dir = new File(Environment.getExternalStorageDirectory() + "/foldername/"); if (!dir.exists()){ dir.mkdirs(); } File sdCardFile = new File(Environment.getExternalStorageDirectory() + "/foldername/" + fileName ); int num = 1; String fileNameAux = fileName; while (sdCardFile.exists()){ fileNameAux = fileName+"_"+num; sdCardFile = new File(Environment.getExternalStorageDirectory() + "/foldername/" + fileNameAux); num++; }

Esto también controla que ese archivo existe y agrega un número al final del nombre del nuevo archivo para guardarlo.

¡Espero eso ayude!

EDITAR: Lo siento, olvidé que tienes que pedirlo <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> en tu manifiesto (o programáticamente si lo prefieres de Marshmallow)


Este tema es un poco antiguo, pero estaba buscando una solución y después de investigar un poco, encontré el siguiente código para recuperar una lista de puntos de montaje "externos" disponibles que, según mi conocimiento, funcionan en muchos dispositivos diferentes.

Básicamente, lee los puntos de montaje disponibles, filtra los no válidos, prueba el resto si son accesibles y los agrega si se cumplen todas las condiciones.

Por supuesto, los permisos requeridos deben otorgarse antes de invocar el código.

// Notice: FileSystemDevice is just my own wrapper class. Feel free to replace it with your own. private List<FileSystemDevice> getDevices() { List<FileSystemDevice> devices = new ArrayList<>(); // Add default external storage if available. File sdCardFromSystem = null; switch(Environment.getExternalStorageState()) { case Environment.MEDIA_MOUNTED: case Environment.MEDIA_MOUNTED_READ_ONLY: case Environment.MEDIA_SHARED: sdCardFromSystem = Environment.getExternalStorageDirectory(); break; } if (sdCardFromSystem != null) { devices.add(new FileSystemDevice(sdCardFromSystem)); } // Read /proc/mounts and add all mount points that are available // and are not "special". Also, check if the default external storage // is not contained inside the mount point. try { FileInputStream fs = new FileInputStream("/proc/mounts"); String mounts = IOUtils.toString(fs, "UTF-8"); for(String line : mounts.split("/n")) { String[] parts = line.split(" "); // parts[0] - mount type // parts[1] - mount point if (parts.length > 1) { try { // Skip "special" mount points and mount points that can be accessed // directly by Android''s functions. if (parts[0].equals("proc")) { continue; } if (parts[0].equals("rootfs")) { continue; } if (parts[0].equals("devpts")) { continue; } if (parts[0].equals("none")) { continue; } if (parts[0].equals("sysfs")) { continue; } if (parts[0].equals("selinuxfs")) { continue; } if (parts[0].equals("debugfs")) { continue; } if (parts[0].equals("tmpfs")) { continue; } if (parts[1].equals(Environment.getRootDirectory().getAbsolutePath())) { continue; } if (parts[1].equals(Environment.getDataDirectory().getAbsolutePath())) { continue; } if (parts[1].equals(Environment.getExternalStorageDirectory().getAbsolutePath())) { continue; } // Verify that the mount point is accessible by listing its content. File file = new File(parts[1]); if (file.listFiles() != null) { try { // Get canonical path for case it''s just symlink to another mount point. String devPath = file.getCanonicalPath(); for(FileSystemDevice device : devices) { if (!devices.contains(devPath)) { devices.add(new FileSystemDevice(new File(devPath))); } } } catch (Exception e) { // Silently skip the exception as it can only occur if the mount point is not valid. e.printStackTrace(); } } } catch (Exception e) { // Silently skip the exception as it can only occur if the mount point is not valid. e.printStackTrace(); } } } fs.close(); } catch (FileNotFoundException e) { // Silently skip the exception as it can only occur if the /proc/mounts file is unavailable. // Possibly, another detection method can be called here. e.printStackTrace(); } catch (IOException e) { // Silently skip the exception as it can only occur if the /proc/mounts file is unavailable. // Possibly, another detection method can be called here. e.printStackTrace(); } return devices; }


Para las versiones debajo de Marshmallow, puede otorgar directamente los permisos en el manifiesto.

Pero para dispositivos con Marshmallow y superior, debe otorgar los permisos en tiempo de ejecución.

Mediante el uso

Environment.getExternalStorageDirectory();

puede acceder directamente a la tarjeta SD externa (montada) Espero que esto ayude.


Además de todas las otras buenas respuestas, podría agregar un poco más a esta pregunta para que pueda brindar una cobertura más amplia para los lectores. En mi respuesta aquí, usaría 2 recursos contables para presentar el almacenamiento externo.

El primer recurso es de Android Programming, The Big Nerd Ranch Guide 2nd edition , capítulo 16, página 294.

El libro describe los métodos básicos y externos de archivo y directorio. Intentaré hacer un resumen de lo que podría ser relevante para su pregunta.

La siguiente parte del libro:

Almacenamiento externo

Su foto necesita más que un lugar en la pantalla. Las imágenes de tamaño completo son demasiado grandes para pegarse dentro de una base de datos SQLite, mucho menos un Intent . Necesitarán un lugar para vivir en el sistema de archivos de su dispositivo. Normalmente, los pondría en su almacenamiento privado. Recuerde que utilizó su almacenamiento privado para guardar su base de datos SQLite. Con métodos como Context.getFileStreamPath(String) y Context.getFilesDir() , también puede hacer lo mismo con archivos normales (que vivirán en una subcarpeta adyacente a la subcarpeta de bases de datos en la que vive su base de datos SQLite)

Archivo básico y métodos de directorio en contexto

| Method | |---------------------------------------------------------------------------------------| |File getFilesDir() | | - Returns a handle to the directory for private application files. | | | |FileInputStream openFileInput(String name) | | - Opens an existing file for input (relative to the files directory). | | | |FileOutputStream openFileOutput(String name, int mode) | | - Opens a file for output, possibly creating it (relative to the files directory). | | | |File getDir(String name, int mode) | | - Gets (and possibly creates) a subdirectory within the files directory. | | | |String[] fileList() | | - Gets a list of file names in the main files directory, such as for use with | | openFileInput(String). | | | |File getCacheDir() | | - Returns a handle to a directory you can use specifically for storing cache files. | | You should take care to keep this directory tidy and use as little space as possible|

Si está almacenando archivos que solo su aplicación actual necesita usar, estos métodos son exactamente lo que necesita.

Por otro lado, si necesita otra aplicación para escribir en esos archivos, no tiene suerte: si bien hay un indicador Context.MODE_WORLD_READABLE que puede pasar a openFileOutput(String, int) , está en desuso y no es completamente confiable en sus efectos en dispositivos más nuevos. Si está almacenando archivos para compartir con otras aplicaciones o recibe archivos de otras aplicaciones (archivos como imágenes almacenadas), debe almacenarlos en un almacenamiento externo.

Hay dos tipos de almacenamiento externo: primario y todo lo demás. Todos los dispositivos Android tienen al menos una ubicación para almacenamiento externo: la ubicación principal, que se encuentra en la carpeta devuelta por Environment.getExternalStorageDirectory() . Puede ser una tarjeta SD, pero hoy en día se integra más comúnmente en el dispositivo. Algunos dispositivos pueden tener almacenamiento externo adicional. Eso caería bajo "todo lo demás".

El contexto también proporciona bastantes métodos para acceder al almacenamiento externo. Estos métodos proporcionan formas fáciles de acceder a su almacenamiento externo primario y formas más o menos sencillas de acceder a todo lo demás. Todos estos métodos almacenan archivos en lugares disponibles públicamente, así que tenga cuidado con ellos.

Archivo externo y métodos de directorio en contexto

| Method | | --------------------------------------------------------------------------------------| |File getExternalCacheDir() | | - Returns a handle to a cache folder in primary external storage. Treat it like you do| | getCacheDir(), except a little more carefully. Android is even less likely to clean | | up this folder than the private storage one. | | | |File[] getExternalCacheDirs() | | - Returns cache folders for multiple external storage locations. | | | |File getExternalFilesDir(String) | | - Returns a handle to a folder on primary external storage in which to store regular | | files. If you pass in a type String, you can access a specific subfolder dedicated | | to a particular type of content. Type constants are defined in Environment, where | | they are prefixed with DIRECTORY_. | | For example, pictures go in Environment.DIRECTORY_PICTURES. | | | |File[] getExternalFilesDirs(String) | | - Same as getExternalFilesDir(String), but returns all possible file folders for the | | given type. | | | |File[] getExternalMediaDirs() | | - Returns handles to all the external folders Android makes available for storing | | media – pictures, movies, and music. What makes this different from calling | | getExternalFilesDir(Environment.DIRECTORY_PICTURES) is that the media scanner | | automatically scans this folder. The media scanner makes files available to | | applications that play music, or browse movies and photos, so anything that you | | put in a folder returned by getExternalMediaDirs() will automatically appear in | | those apps. |

Técnicamente, las carpetas externas proporcionadas anteriormente pueden no estar disponibles, ya que algunos dispositivos usan una tarjeta SD extraíble para almacenamiento externo. En la práctica, esto rara vez es un problema, porque casi todos los dispositivos modernos tienen almacenamiento interno no extraíble para su almacenamiento "externo". Por lo tanto, no vale la pena llegar a extremos para dar cuenta de ello. Pero recomendamos incluir un código simple para protegerse contra la posibilidad, lo que hará en un momento.

Permiso de almacenamiento externo

En general, necesita un permiso para escribir o leer desde un almacenamiento externo. Los permisos son valores de cadena bien conocidos que coloca en su manifiesto utilizando la etiqueta <uses-permission> . Le dicen a Android que quieres hacer algo que Android quiere que pidas permiso.

Aquí, Android espera que pidas permiso porque quiere imponer cierta responsabilidad. Le dice a Android que necesita acceder al almacenamiento externo, y Android le dirá al usuario que esta es una de las cosas que hace su aplicación cuando intenta instalarla. De esa manera, nadie se sorprende cuando comienzas a guardar cosas en su tarjeta SD.

En Android 4.4, KitKat, aflojaron esta restricción. Dado que Context.getExternalFilesDir(String) devuelve una carpeta que es específica para su aplicación, tiene sentido que desee leer y escribir archivos que viven allí. Entonces, en Android 4.4 (API 19) y versiones posteriores, no necesita este permiso para esta carpeta. (Pero aún lo necesita para otros tipos de almacenamiento externo).

Agregue una línea a su manifiesto que solicite el permiso para leer el almacenamiento externo, pero solo hasta API Listado 16.5 Solicitud de permiso de almacenamiento externo ( AndroidManifest.xml )

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.bignerdranch.android.criminalintent" > <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="18" />

El atributo maxSdkVersion hace que su aplicación solo solicite este permiso en versiones de Android que son anteriores a API 19, Android KitKat. Tenga en cuenta que solo solicita leer el almacenamiento externo. También hay un permiso WRITE_EXTERNAL_STORAGE , pero no lo necesita. No escribirá nada en el almacenamiento externo: la aplicación de la cámara lo hará por usted

El segundo recurso es este link lea todo, pero también puede saltar a la sección Uso del almacenamiento externo .

Referencia:

Más material de lectura:

Descargo de responsabilidad: Esta información fue tomada de la Programación de Android: la guía Big Nerd Ranch con permiso de los autores. Para obtener más información sobre este libro o para comprar una copia, visite bignerdranch.com.


Creo que hay dos métodos para lograr esto:

MÉTODO 1: ( NO funciona en 6.0 y superior, debido a cambios en los permisos)

He estado usando este método durante años en muchas versiones de dispositivos sin ningún problema. El crédito se debe a la fuente original, ya que no fui yo quien lo escribió.

Devolverá todos los medios montados (incluidas las tarjetas SD reales) en una lista de ubicaciones de directorio de cadenas. Con la lista, puede preguntarle al usuario dónde guardar, etc.

Puedes llamarlo con lo siguiente:

HashSet<String> extDirs = getStorageDirectories();

Método:

/** * Returns all the possible SDCard directories */ public static HashSet<String> getStorageDirectories() { 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(); } // parse output final String[] lines = s.split("/n"); for (String line : lines) { if (!line.toLowerCase().contains("asec")) { if (line.matches(reg)) { String[] parts = line.split(" "); for (String part : parts) { if (part.startsWith("/")) if (!part.toLowerCase().contains("vold")) out.add(part); } } } } return out; }

MÉTODO 2:

Use la biblioteca de soporte v4

import android.support.v4.content.ContextCompat;

Simplemente llame al siguiente para obtener una lista de ubicaciones de almacenamiento de File .

File[] list = ContextCompat.getExternalFilesDirs(myContext, null);

Sin embargo, las ubicaciones difieren en el uso.

Devuelve rutas absolutas a directorios específicos de la aplicación en todos los dispositivos de almacenamiento externo donde la aplicación puede colocar archivos persistentes que posee. Estos archivos son internos de la aplicación y, por lo general, no son visibles para el usuario como medios.

Los dispositivos de almacenamiento externo que se devuelven aquí se consideran una parte permanente del dispositivo, incluido el almacenamiento externo emulado y las ranuras de medios físicos, como las tarjetas SD en el compartimento de la batería. Las rutas devueltas no incluyen dispositivos transitorios, como unidades flash USB.

Una aplicación puede almacenar datos en cualquiera o en todos los dispositivos devueltos. Por ejemplo, una aplicación puede elegir almacenar archivos grandes en el dispositivo con la mayor cantidad de espacio disponible

ContextCompat.getExternalFilesDirs

Son como archivos específicos de la aplicación. Oculto de otras aplicaciones.


Solo otra respuesta. Esta respuesta solo muestra 5.0+ porque creo que la respuesta de Doomknight publicada aquí es la mejor manera de hacerlo para Android 4.4 y versiones posteriores.

Esto se publicó originalmente aquí ( ¿hay alguna forma de obtener el tamaño de la tarjeta SD en Android? ) Por mí para obtener el tamaño de la tarjeta SD externa en Android 5.0+

Para obtener la tarjeta SD externa como un File :

public File getExternalSdCard() { File externalStorage = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { File storage = new File("/storage"); if(storage.exists()) { File[] files = storage.listFiles(); for (File file : files) { if (file.exists()) { try { if (Environment.isExternalStorageRemovable(file)) { externalStorage = file; break; } } catch (Exception e) { Log.e("TAG", e.toString()); } } } } } else { // do one of many old methods // I believe Doomsknight''s method is the best option here } return externalStorage; }

Nota: Solo obtengo la "primera" tarjeta SD externa, sin embargo, puede modificarla y devolver ArrayList<File> lugar de File y dejar que el ciclo continúe en lugar de llamar a break después de encontrar la primera.