En mi aplicación, necesito almacenar muchas imágenes en el almacenamiento del dispositivo. Dichos archivos tienden a ocupar 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, por tarjeta SD me refiero a la tarjeta SD externa y montable y no al almacenamiento externo , pero las aplicaciones de administración de archivos logran escribir en 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
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:
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 obtengo 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
Respuestas:
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.
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
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 :
/storage/extSdCard/Android/data/com.myapp.example/files
.2. Respuesta actualizada.
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 como
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT
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" /> // this line is added via root in the link to fix it. </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(); } // 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 externoLeer la explicación de la marca Murphy y recomendados Dianne Hackborn y Dave Smith mensajes
4. Android 4.4 KitKat presenta Storage Access Framework (SAF) .
Ignore la siguiente nota debido a errores, pero intente usar
ContextCompat.getExternalFilesDirs()
:Lea también la explicación de Paolo Rovelli e intente usar la solución de Jeff Sharkey desde KitKat:
Con KitKat, sus posibilidades de obtener una "solución completa" sin rootear son prácticamente nulas:
5. Android 5.0 introdujo cambios y la clase auxiliar DocumentFile .
getStorageState
Agregado en API 19, obsoleto en API 21, usegetExternalStorageState(File)
6. Android 6.0 Marshmallow presenta un nuevo modelo de permisos en tiempo de ejecución .
Solicite permisos en tiempo de ejecución si API nivel 23+ y lea Solicitud de permisos en tiempo de ejecución
7. Android 7.0 proporciona una API simplificada para acceder a directorios de almacenamiento externo.
Lea las publicaciones de Mark Murphy: Tenga cuidado con el acceso al directorio con alcance . Quedó obsoleto en Android Q :
8. Android 8.0 Oreo .. Cambios en Android Q Beta.
Actualización: una versión beta anterior de Android Q reemplazó temporalmente los permisos
READ_EXTERNAL_STORAGE
yWRITE_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
yREAD_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 (?)
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
fuente
Creo que hay dos métodos para lograrlo:
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 problemas. 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 del directorio de cadenas. Con la lista, puede preguntarle al usuario dónde guardar, etc.
Puede llamarlo con lo siguiente:
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:
Utilice la biblioteca de soporte v4
import android.support.v4.content.ContextCompat;
Simplemente llame a los siguientes para obtener una lista de
File
ubicaciones de almacenamiento.File[] list = ContextCompat.getExternalFilesDirs(myContext, null);
Sin embargo, las ubicaciones difieren en el uso.
Más información sobre ContextCompat
Son como archivos específicos de una aplicación. Oculto de otras aplicaciones.
fuente
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 anteriores.
Esto se publicó originalmente aquí ( ¿Hay alguna manera 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
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 regresar en
ArrayList<File>
lugar deFile
dejar que el ciclo continúe en lugar de llamarbreak
después de encontrar la primera.fuente
isExternalStorageRemovable
referencia developer.android.com/reference/android/os/…isExternalStorageRemovable
es solo para> = 21, ¿es posible determinar si el almacenamiento es extraíble (tarjeta microsd) en <= 19 dispositivos API, implementando un método similar de alguna manera?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 a los lectores. En mi respuesta aquí, usaría 2 recursos contables para presentar Almacenamiento externo.
El primer recurso es de Programación de Android, The Big Nerd Ranch Guide 2nd edition , capítulo 16, página 294.
El libro describe los métodos básicos y externos de archivos y directorios. Intentaré hacer un resumen de lo que podría ser relevante para su pregunta.
La siguiente parte del libro:
Almacenamiento externo
Tu foto necesita más que un lugar en la pantalla. Las imágenes de tamaño completo son demasiado grandes para quedarse dentro de una base de datos SQLite, y mucho menos un archivo
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 comoContext.getFileStreamPath(String)
yContext.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 reside su base de datos SQLite)Métodos básicos de archivos y directorios 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 una
Context.MODE_WORLD_READABLE
bandera a la que puede pasaropenFileOutput(String, int)
, está obsoleta y no es completamente confiable en sus efectos en los dispositivos más nuevos. Si almacena 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 el almacenamiento externo: la ubicación principal, que se encuentra en la carpeta devuelta por
Environment.getExternalStorageDirectory()
. Esta 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 se incluiría en "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 principal y formas bastante sencillas de acceder a todo lo demás. Todos estos métodos también almacenan archivos en lugares disponibles públicamente, así que tenga cuidado con ellos.
Métodos de archivos y directorios externos 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 hacer todo lo posible para explicarlo. 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 pones en tu manifiesto usando la
<uses-permission>
etiqueta. Le dicen a Android que quieres hacer algo para lo que Android quiere que pidas permiso.En este caso, 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 forma, 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 de su aplicación, tiene sentido que desee poder 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 el Listado de API 16.5 Solicitar 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 anteriores a API 19, Android KitKat. Tenga en cuenta que solo solicita leer el almacenamiento externo. También hay un
WRITE_EXTERNAL_STORAGE
permiso, pero no lo necesita. No escribirás nada en el almacenamiento externo: la aplicación de la cámara lo hará por tiEl segundo recurso es este enlace para leerlo todo, pero también puede saltar a la sección Uso de almacenamiento externo .
Referencia:
Más material de lectura:
fuente
Este tema es un poco antiguo pero estaba buscando una solución y, después de investigar un poco, vine con 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, se deben conceder los permisos necesarios 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; }
fuente
Esta es una forma de crear un nuevo archivo en el almacenamiento externo (tarjeta SD si está presente en el dispositivo o en el almacenamiento externo del dispositivo si no lo está). 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 el 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)fuente
Para versiones por debajo de Marshmallow, puede otorgar los permisos directamente en el manifiesto.
Pero para dispositivos con Marshmallow y superior, debe otorgar los permisos en tiempo de ejecución.
Mediante el uso
puede acceder directamente a la tarjeta SD externa (montada) Espero que esto ayude.
fuente
Environment.getExternalStorageDirectory();
le dará la ruta a la tarjeta SD interna del dispositivo.