Antecedentes
En Android 4.4 (KitKat), Google ha restringido bastante el acceso a la tarjeta SD.
A partir de Android Lollipop (5.0), los desarrolladores pueden usar una nueva API que le pide al usuario que confirme para permitir el acceso a carpetas específicas, como está escrito en esta publicación de Grupos de Google .
El problema
La publicación le dirige a visitar dos sitios web:
Esto parece un ejemplo interno (tal vez se muestre en las demostraciones de API más adelante), pero es bastante difícil entender qué está pasando.
Esta es la documentación oficial de la nueva API, pero no brinda suficientes detalles sobre cómo usarla.
Esto es lo que te dice:
Si realmente necesita acceso completo a un subárbol completo de documentos, comience iniciando ACTION_OPEN_DOCUMENT_TREE para permitir que el usuario elija un directorio. Luego, pase el getData () resultante a fromTreeUri (Contexto, Uri) para comenzar a trabajar con el árbol seleccionado por el usuario.
Mientras navega por el árbol de instancias de DocumentFile, siempre puede usar getUri () para obtener el Uri que representa el documento subyacente para ese objeto, para usar con openInputStream (Uri), etc.
Para simplificar su código en dispositivos que ejecutan KITKAT o una versión anterior, puede usar fromFile (Archivo) que emula el comportamiento de un DocumentsProvider.
Las preguntas
Tengo algunas preguntas sobre la nueva API:
- ¿Cómo lo usas realmente?
- Según la publicación, el sistema operativo recordará que la aplicación recibió un permiso para acceder a los archivos / carpetas. ¿Cómo verifica si puede acceder a los archivos / carpetas? ¿Existe una función que me devuelva la lista de archivos / carpetas a los que puedo acceder?
- ¿Cómo maneja este problema en Kitkat? ¿Es parte de la biblioteca de soporte?
- ¿Hay una pantalla de configuración en el sistema operativo que muestre qué aplicaciones tienen acceso a qué archivos / carpetas?
- ¿Qué sucede si se instala una aplicación para varios usuarios en el mismo dispositivo?
- ¿Existe alguna otra documentación / tutorial sobre esta nueva API?
- ¿Se pueden revocar los permisos? Si es así, ¿se está enviando una intención a la aplicación?
- ¿Pedir el permiso funcionaría de forma recursiva en una carpeta seleccionada?
- ¿El uso del permiso también permitiría dar al usuario la posibilidad de realizar una selección múltiple por elección del usuario? ¿O la aplicación necesita indicar específicamente a la intención qué archivos / carpetas permitir?
- ¿Hay alguna forma en el emulador de probar la nueva API? Quiero decir, tiene una partición de tarjeta SD, pero funciona como el almacenamiento externo principal, por lo que ya se otorga todo el acceso (con un simple permiso).
- ¿Qué sucede cuando el usuario reemplaza la tarjeta SD por otra?
fuente
Respuestas:
Muchas buenas preguntas, profundicemos :)
¿Como lo usas?
Aquí hay un gran tutorial para interactuar con Storage Access Framework en KitKat:
https://developer.android.com/guide/topics/providers/document-provider.html#client
Interactuar con las nuevas API en Lollipop es muy similar. Para pedirle al usuario que elija un árbol de directorios, puede iniciar una intención como esta:
Luego, en su onActivityResult (), puede pasar el Uri elegido por el usuario a la nueva clase auxiliar DocumentFile. Aquí hay un ejemplo rápido que enumera los archivos en el directorio seleccionado y luego crea un nuevo archivo:
El Uri devuelto por
DocumentFile.getUri()
es lo suficientemente flexible como para usarlo con distintas API de plataforma. Por ejemplo, puede compartirloIntent.setData()
conIntent.FLAG_GRANT_READ_URI_PERMISSION
.Si desea acceder a ese Uri desde el código nativo, puede llamar
ContentResolver.openFileDescriptor()
y luego usarParcelFileDescriptor.getFd()
odetachFd()
para obtener un entero descriptor de archivo POSIX tradicional.¿Cómo verifica si puede acceder a los archivos / carpetas?
De forma predeterminada, los Uris devueltos a través de los intentos de Storage Access Frameworks no se conservan durante los reinicios. La plataforma "ofrece" la capacidad de conservar el permiso, pero aún necesita "tomar" el permiso si lo desea. En nuestro ejemplo anterior, llamaría:
Siempre puede averiguar a qué concesiones persistentes tiene acceso su aplicación a través de la
ContentResolver.getPersistedUriPermissions()
API. Si ya no necesita acceso a un Uri persistente, puede liberarlo conContentResolver.releasePersistableUriPermission()
.¿Está disponible en KitKat?
No, no podemos agregar nuevas funciones de manera retroactiva a versiones anteriores de la plataforma.
¿Puedo ver qué aplicaciones tienen acceso a archivos / carpetas?
Actualmente no hay una interfaz de usuario que muestre esto, pero puede encontrar los detalles en la sección de
adb shell dumpsys activity providers
salida "Permisos Uri otorgados" .¿Qué sucede si se instala una aplicación para varios usuarios en el mismo dispositivo?
Las concesiones de permisos Uri están aisladas por usuario, al igual que todas las demás funciones de la plataforma multiusuario. Es decir, la misma aplicación que se ejecuta con dos usuarios diferentes no tiene concesiones de permisos Uri compartidas o superpuestas.
¿Se pueden revocar los permisos?
El DocumentProvider de respaldo puede revocar el permiso en cualquier momento, como cuando se elimina un documento basado en la nube. La forma más común de descubrir estos permisos revocados es cuando desaparecen de los
ContentResolver.getPersistedUriPermissions()
mencionados anteriormente.Los permisos también se revocan cada vez que se borran los datos de la aplicación para cualquiera de las aplicaciones involucradas en la concesión.
¿Pedir el permiso funcionaría de forma recursiva en una carpeta seleccionada?
Sí, la
ACTION_OPEN_DOCUMENT_TREE
intención le brinda acceso recursivo a archivos y directorios existentes y recién creados.¿Esto permite una selección múltiple?
Sí, la selección múltiple ha sido compatible desde KitKat, y puede permitirla configurando
EXTRA_ALLOW_MULTIPLE
al iniciar suACTION_OPEN_DOCUMENT
intento. Puede utilizarIntent.setType()
oEXTRA_MIME_TYPES
para limitar los tipos de archivos que se pueden seleccionar:http://developer.android.com/reference/android/content/Intent.html#ACTION_OPEN_DOCUMENT
¿Hay alguna forma en el emulador de probar la nueva API?
Sí, el dispositivo de almacenamiento compartido principal debería aparecer en el selector, incluso en el emulador. Si su aplicación sólo utiliza el Marco de almacenamiento de acceso para acceder a almacenamiento compartido, que ya no necesita los
READ/WRITE_EXTERNAL_STORAGE
permisos en absoluto y puede eliminarlos o utilizar laandroid:maxSdkVersion
función para solicitar sólo a ellos en las versiones de plataforma de mayor edad.¿Qué sucede cuando el usuario reemplaza la tarjeta SD por otra?
Cuando se trata de medios físicos, el UUID (como el número de serie FAT) del medio subyacente siempre se graba en el Uri devuelto. El sistema utiliza esto para conectarlo a los medios que el usuario seleccionó originalmente, incluso si el usuario intercambia los medios entre varias ranuras.
Si el usuario cambia una segunda tarjeta, deberá solicitarlo para obtener acceso a la nueva tarjeta. Dado que el sistema recuerda las subvenciones por UUID, continuará teniendo acceso previamente otorgado a la tarjeta original si el usuario la reintroduce más tarde.
http://en.wikipedia.org/wiki/Volume_serial_number
fuente
En mi proyecto de Android en Github, vinculado a continuación, puede encontrar código de trabajo que permite escribir en extSdCard en Android 5. Se asume que el usuario da acceso a toda la tarjeta SD y luego le permite escribir en todas partes en esta tarjeta. (Si desea tener acceso solo a archivos individuales, las cosas se vuelven más fáciles).
Fragmentos de código principal
Activación del marco de acceso al almacenamiento:
Manejo de la respuesta de Storage Access Framework:
Obtener un outputStream para un archivo a través de Storage Access Framework (haciendo uso de la URL almacenada, asumiendo que esta es la URL de la carpeta raíz de la tarjeta SD externa)
Esto utiliza los siguientes métodos de ayuda:
Referencia al código completo
https://github.com/jeisfeld/Augendiagnose/blob/master/AugendiagnoseIdea/augendiagnoseLib/src/main/java/de/jeisfeld/augendiagnoselib/fragments/SettingsFragment.java#L521
y
https://github.com/jeisfeld/Augendiagnose/blob/master/AugendiagnoseIdea/augendiagnoseLib/src/main/java/de/jeisfeld/augendiagnoselib/util/imagefile/FileUtil.java
fuente
File
254 veces. ¿Te imaginas arreglar eso? Android se está convirtiendo en una pesadilla para los desarrolladores con su total falta de compatibilidad con versiones anteriores. Todavía no encontré ningún lugar donde explicaran por qué Google tomó todas estas decisiones estúpidas con respecto al almacenamiento externo. Algunos afirman "seguridad", pero por supuesto es una tontería, ya que cualquier aplicación puede estropear el almacenamiento interno. Supongo que intentar forzarnos a utilizar sus servicios en la nube. Afortunadamente, el enraizamiento resuelve los problemas ... al menos para Android <6 ....Es solo una respuesta complementaria.
Después de crear un nuevo archivo, es posible que deba guardar su ubicación en su base de datos y leerlo mañana. Puede leer recuperarlo nuevamente usando este método:
fuente