1. Antecedentes
El capítulo anterior [Serie Android Framework] Capítulo 16 Marco de acceso al almacenamiento (SAF) analizó principalmente el marco de acceso al almacenamiento (SAF) introducido en Android 4.4 存储访问框架(SAF)
. En este capítulo analizamos Android10(Q)
el almacenamiento relacionado y comprendemos sus métodos de almacenamiento restringidos.
Google
Para brindar a los usuarios más control sobre sus archivos y limitar el desorden de archivos, la forma de acceder al almacenamiento de su dispositivo Android Q
ha cambiado .App
Antes de 1.1 Android Q
Siempre que el programa obtenga READ_EXTERNAL_STORAGE
permiso, puede leer libremente el directorio público del almacenamiento externo;
siempre que el programa obtenga WRITE_EXTERNAL_STORAGE
permiso, puede crear libremente nuevos archivos o carpetas en el directorio público escrito en el almacenamiento externo.
1.2 Android Q y posteriores
Así lo propuso Google en Android Q 分区存储
, con la intención de limitar el uso de directorios públicos en almacenamiento externo por parte de los programas .
El almacenamiento particionado no tiene ningún impacto ni en los directorios privados de almacenamiento interno ni en los directorios privados de almacenamiento externo.
2 modo caja de arena
2.1 Android Q estipula que la aplicación tiene dos vistas de modo de espacio de almacenamiento: Vista heredada y Vista filtrada.
2.1.1 Vista heredada (modo de compatibilidad)
Cuando el proyecto targetSdkVersion <= 28, el modo de compatibilidad se usa de forma predeterminada en el dispositivo AndroidQ, el método de almacenamiento es el mismo que Android Q
antes App
y el acceso Sdcard
es completo, con permisos de acceso completos.
2.1.2 Vista filtrada (modo sandbox)
Cuando el proyecto targetSdkVersion> 28, el dispositivo AndroidQ App
solo puede acceder directamente App-specific
a los archivos del directorio y no tiene permiso para acceder App-specific
a archivos externos. El acceso a otros directorios sólo se puede proporcionar a través de MediaStore
, SAF
u otras aplicaciones ContentProvider
.
2.2 Scoped Storage divide el espacio de almacenamiento en dos partes
2.2.1 Directorio público de la tarjeta SD: Descargas, Documentos, Imágenes, DCIM, Películas, Música, Tonos de llamada
- Los archivos en el directorio público
App
no se eliminarán después de la desinstalación - Se puede acceder a través de
SAF
laMediaStore
interfaz.
2.2…2 datos/directorio privado de datos (directorio específico de la aplicación)
- Para
Filtered View App
,App-specific
solo usted puede acceder directamente al directorio App
Desinstale y los datos se borrarán.
2.3 Impacto de la compatibilidad
Scoped Storage
Tiene un gran impacto en App访问存储方式
y App数据存放
.App间数据共享
2.4 Adaptación
Para una adaptación específica, consulte la documentación oficial.
2.4.1 Vista de aplicación en ejecución
El sistema determina el modo de ejecución de la aplicación mediante lo siguiente:
App TargetSDK > 28
,por defectoFiltered View(沙箱模式)
App TargetSDK <= 28
, declara el permiso READ_EXTERNAL_STORAGE o WRITE_EXTERNAL_STORAGE, predeterminadoLegacy View(兼容模式)
- La aplicación puede configurar requestLegacyExternalStorage a través de AndroidManifest.xml y seleccionar el método correspondiente:
declaradoREAD_EXTERNAL_STORAGE
,WRITE_EXTERNAL_STORAGE
permiso (ignorado si no declarado):
requestLegacyExternalStorage=true
significaLegacy View(兼容模式)
requestLegacyExternalStorage=false
Filtered View(沙箱模式)
(注意:该方式Android11后失效了)
- Las aplicaciones del sistema pueden solicitar
android.permission.WRITE_MEDIA_STORAGE
permisos del sistema y también tener permisos completos de espacio de almacenamiento y pueden acceder a todos los archivos. Sin embargo, en la prueba CTS, solo pueden postularse las aplicaciones que no tienen interacción con el usuario y son visibles. Referencia específica《Android Bootcamp 2019 - Privacy Overview.pdf》
.
- La aplicación es propiedad cuando se cumplen las siguientes condiciones
: ① DeclaraciónINSTALL_PACKAGES
o动态申请INSTALL_PACKAGES
permiso
② PropiedadWRITE_EXTERNAL_STORAGE权限
③ Propiedad de la aplicación外置存储空间Read、Write权限
.
Sin embargo,Environment.isExternalStorageLegacy
a juzgar por la interfaz, la devolución no es necesariamente una Vista heredada.
2.4.2 Determinar el modo de ejecución actual de la aplicación
Para determinar en qué modo se está ejecutando la aplicación actual, puede utilizar esta API para determinar:
Environment.isExternalStorageLegacy();
2.5 Lectura y redacción de directorios públicos
Una vez iniciada la aplicación Filtered View
, solo puede acceder a sí misma directamente App-specific目录
, por lo que Android Q
se proporcionan dos métodos para acceder al directorio público:
2.5.1 Uri definido por MediaStore
MediaStore
Se proporcionan los siguientes tipos de acceso Uri
, y el propósito del acceso se logra mediante la búsqueda de los datos Uri correspondientes.
Cada uno de los siguientes tipos se divide en tres tipos Uri
, Internal
, External
, 可移动存储
:
Audio
- Interno: MediaStore.Audio.Media.INTERNAL_CONTENT_URI
contenido://media/internal/audio/media。- Externo: MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
contenido://media/external/audio/media。- Almacenamiento extraíble: MediaStore.Audio.Media.getContentUri
content://media//audio/media.
Video
- Interno: MediaStore.Video.Media.INTERNAL_CONTENT_URI
contenido://media/internal/video/media。- Externo: MediaStore.Video.Media.EXTERNAL_CONTENT_URI
contenido://media/external/video/media。- Almacenamiento extraíble: MediaStore.Video.Media.getContentUri
content://media//video/media.
Imagen
- Interno: MediaStore.Images.Media.INTERNAL_CONTENT_URI
contenido: //media/internal/images/media。- Externo: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
contenido://media/external/images/media。- Almacenamiento extraíble: MediaStore.Images.Media.getContentUri
content://media//images/media.
Archivo
- Tienda de medios. Files.Media.getContentUri
content://media//file。
Descargas
- Interno: MediaStore.Downloads.INTERNAL_CONTENT_URI
contenido://media/internal/downloads。- Externo: MediaStore.Downloads.EXTERNAL_CONTENT_URI
contenido://media/external/downloads。- Almacenamiento extraíble: MediaStore.Downloads.getContentUri
content://media//downloads.
2.5.1.1 Obtener todos los volúmenes
Para los Uri descritos anteriormente, getContentUri
cómo obtenerlos todos se puede hacer de la siguiente manera:
for(String volume:MediaStore.getExternalVolumeNames(this)){
MediaStore.Audio.Media.getContentUri(volume);
}
2.5.1.2 Relación entre Uri y directorio público
MediaProvider
Para que la aplicación se almacene en un archivo de directorio público, está determinado por el Uri en el método. La ruta relativa se ContentResolver insert
muestra en la siguiente tabla y el archivo completo es: content://media//<ruta de Uri>.<Uri路径>
2.5.1.3 Permisos
MediaStore
A través de diferentes métodos Uri
, los usuarios pueden agregar, eliminar (si los archivos no se pueden eliminar a través de File Uri, debe usar la interfaz SAF) y modificar.
Los permisos correspondientes de la App son los siguientes:
2.5.1.4 Consultar archivos
Al ContentResolver
consultar contenido diferente según diferentes Uri:
try (Cursor c = getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, null, null, null)) {
while (c.moveToNext()) {
Uri contentUri =
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, c.getLong(0));
}
}
PD: Durante MediaStore.Files
el proceso Query
, solo se mostrarán imágenes, videos y archivos de audio.
2.5.1.5 Lectura de archivos
A través ContentResolver query
de la interfaz, cómo leer el archivo después de encontrarlo se puede hacer de la siguiente manera:
- A través de
ContentResolver openFileDescriptor
la interfaz, seleccione el método de apertura correspondiente,
por ejemplo, "r" significa lectura, "w" significa escritura y elParcelFileDescriptor
tipo de retornoFD
. - Se accede
Thumbnail
a través deContentResolver loadThumbnail
la interfaz
Al pasar el tamaño,MediaProvider
se devuelve el tamaño especificadoThumbnail
. - El código nativo accede a los archivos.
Si el código nativo necesita acceder a los archivos, puede consultar los siguientes métodos:
①openFileDescriptor
RegresandoParcelFileDescriptor
②ParcelFileDescriptor.detachFd()
LeyendoFD
③FD
PasandoNative
al código de capa
④App
Debe ser responsable de cerrar a través declose
la interfazFD
ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(uri, file0penMode);
if (parcelFd != null) {
int fd = parcelFd.detachFd();
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.
}
2.5.1.6 Crear nuevo archivo
Si necesita almacenar un archivo nuevo en un directorio público, debe ContentResolver insert
usar una interfaz diferente Uri
y elegir almacenarlo en un directorio diferente.
2.5.1.7 Modificar archivos
Si necesita modificar un archivo multimedia, debe ContentResolver query
encontrar el Uri del archivo correspondiente a través de la interfaz.
Si no creó un archivo nuevo usted mismo, debe prestar atención a la descripción en 2.5.1.3 Permisos. Debe solicitarlo WRITE_EXTERNAL_STORAGE权限
o catch RecoverableSecurityException
un cuadro emergente le dará al usuario una opción.
A través de la siguiente interfaz, obtenga el archivo que necesita ser modificado FD
o OutputStream
:
- getContentResolver().openOutputStream(contentUri)
obtiene el OutputStream del archivo correspondiente. - getContentResolver().openFile o getContentResolver().openFileDescriptor
abre el archivo a través de openFile o openFileDescriptor. Debe seleccionar Modo como "w", que indica permiso de escritura. Estas interfaces devuelven un ParcelFileDescriptor.
getContentResolver().openFileDescriptor(contentUri,“w”);
getContentResolver().openFile(contentUri,“w”,null);
2.5.1.8 Eliminar archivos
Elimine archivos a través de la interfaz ContentResolver y el Uri es el Uri de la consulta:
getContentResolver().delete(contentUri,null,null);
2.5.2 A través de la interfaz SAF
2.5.2.1 Introducción a SAF
SAF
, es decir , brindar a los usuarios la posibilidad de abrir y explorar archivos Storage Access Framework
seleccionando diferentes . DocumentsProvider
Podemos mirar atrás al capítulo anterior.
Android proporciona lo siguiente de forma predeterminada DocumentsProvider
:
MediaDocumentsProvider
, ExternalStorageProvider
, DownloadStorageProvider
.
La diferencia entre ellos es:
En esta imagen, hay tres áreas, a saber:
- MediosDocumentosProporcionar
- Descargar proveedor de almacenamiento
- Proveedor de almacenamiento externo
- Proveedor de documentos de terceros
2.5.2.2 Cómo utilizar
Consulte la documentación oficial para obtener más detalles.
El método general es el siguiente:
- Seleccione un solo archivo
- Seleccione el directorio,
el programa de administración de archivos y el programa de limpieza. Puede utilizar este método para obtener todos los permisos de administración para el directorio y los subdirectorios correspondientes. - crear un nuevo archivo
- borrar
DocumentsContract.deleteDocument(getContentResolver(),uri);
- Revisar
①Obtener flujo de salida
getContentResolver().openOutputStream(uri);
②Obtenga el ParcelFileDescriptor grabable
getContentResolver().openFileDescriptor(contentUri,"w");
getContentResolver().openFile (contentUri,"w",null);
Referencia de demostración específica
2.6 Acceder a directorios específicos de la aplicación
Hay dos situaciones de acceso App-specific
, la primera es acceso App自身App-specific目录
y la segunda es acceso 其他App目录文件
.
2.6.1 Directorio específico de la aplicación propia
Android Q, si se inicia la aplicación Filtered View
, solo puede acceder directamente a los archivos en su propio directorio:
- Las interfaces Environment.getExternalStorageDirectory y getExternalStoragePublicDirectory
están obsoletas en Android Q. La aplicación es una vista filtrada y no puede acceder directamente a este directorio. - Acceder
a la aplicación a través de un archivo ("/sdcard/") es una vista filtrada y no puede acceder directamente a este directorio. - Obtenga el directorio específico de la aplicación
Obtenga la interfaz de medios: getExternalMediaDirs
Obtenga la interfaz de caché: getExternalCacheDirs
Obtenga la interfaz de Obb: getObbDirs
Obtenga la interfaz de datos: getExternalFilesDirs
2.6.2 Archivos multimedia en el directorio específico de la aplicación
Archivos multimedia dentro del directorio específico de la aplicación:
- El acceso propio de la aplicación
App-specific
es el mismo que el directorio propio de la aplicación 2.6.1 - Acceso desde otras aplicaciones
① De forma predeterminada , los archivos multimedia que contieneMedia Scanner
no se escanearán . Si es necesario escanear, debe agregarse a la base de datos. El método de acceso es el mismo que 2.5 lectura y escritura de directorios públicos. ②La aplicación se comparte compartiendoApp-specific
MediaScannerConnection.scanFile
MediaProvider
ContentProvider
2.6.3 Otros archivos del directorio de aplicaciones
La aplicación es una vista filtrada. Otras aplicaciones no pueden acceder directamente al directorio privado de la aplicación actual. Debe utilizar el siguiente método:
2.6.3.1 A través del archivo SAF
- Compartir personalización de la aplicación
DocumentsProvider
La personalización de la aplicación DocumentsProvider
requiere los siguientes pasos:
a) EspecificarDocumentsProvider
b) DocumentsProvider
Implementar la interfaz básica:
- Accede a la App
ACTION_OPEN_DOCUMENT
y empieza a navegar
2.6.3.2 Compartir aplicación para implementar FileProvider
Referencia de uso específica de FileProvider
A continuación se muestra un resumen de los pasos generales:
-
Proveedor de archivos de aplicación especificado
-
Especifique la ruta del archivo, el archivo de configuración debe colocarse en res/xml
-
Consigue compartir Uri
-
Establecer permisos y enviar Uri
-
Reciba la aplicación y configure el interfiltro aceptado
-
Recibir y procesar Uri
2.6.3.3 Proveedor privado personalizado de la aplicación
Las aplicaciones se pueden personalizar ContentProvider
, especialmente 内部文件共享
, pero no se espera interacción con la interfaz de usuario.
2.7 Tienda multimedia
2.7.1 Campo MediaStore_data
MediaStore
En , DATA即(_data)字段
comenzó Android Q
a ser abandonado en. Leer y escribir archivos requiere aprobar openFileDescriptor
.
2.7.2 Estado pendiente del archivo MediaStore
En Android Q, MediaStore
se agregó un IS_PENDING Flag
para marcar Pending
el estado del archivo actual.
Otras aplicaciones MediaStore
consultan archivos. Si no setIncludePending
se configura ninguna interfaz, los archivos configurados en estado Pendiente no se pueden consultar. Esto le da a la aplicación acceso exclusivo a este archivo. Se utiliza en algunas situaciones, como durante la descarga: durante la descarga, el archivo está en estado Pendiente y la descarga se completa, y el estado Pendiente del archivo se establece en 0.
2.7.3 MediaColumns.RELATIVE_PATH establece la ruta de almacenamiento
En Android Q, al MediaStore
almacenar archivos en el directorio público, además del directorio de primer nivel de cada espacio de almacenamiento especificado en la relación entre Uri y el directorio público en la Sección 2.5.1.2, el directorio secundario para el almacenamiento se puede especificar a través de MediaColumns. RELATIVE_PATH.Este directorio Se pueden crear múltiples niveles, el código específico es el siguiente:
- El método de inserción de ContentResolver
especifica el directorio de almacenamiento a través de value.put(Media.RELATIVE_PATH,"Pictures/album/family "). Entre ellos, Imágenes es un directorio de primer nivel y álbum/familia es un subdirectorio. - El método de actualización de ContentResolver
especifica el directorio de almacenamiento a través de value.put(Media.RELATIVE_PATH,"Pictures/album/family "). Mediante el método de actualización, se puede mover la ubicación de almacenamiento.
2.7.4 Acceder a los metadatos Exif de la imagen
En Android Q, si la aplicación necesita acceder a los metadatos Exif de la imagen, debe hacer lo siguiente:
- Aplicar
ACCESS_MEDIA_LOCATION权限
- El código de demostración es el
MediaStore.setRequireOriginal返回新Uri
siguiente:
2.7.5 Vista filtrada de aplicaciones, resumen de permisos de acceso
Los permisos de la aplicación para acceder a diferentes directorios se resumen a continuación:
2.7.6 Desinstalación de aplicaciones
Si la aplicación AndroidManifest.xml
declara: android:hasFragileUserData="true"
Al desinstalar la aplicación, se le preguntará si desea conservar los datos de la aplicación:
2.7.7 Migración de datos de aplicaciones
En Android Q, App TargetSDK>=Q
el valor predeterminado es Filtered View
. Si la aplicación es una vista filtrada, implicará la migración de datos; de lo contrario, los datos antiguos quedarán inutilizables. Puede iniciar la migración de datos desde los siguientes aspectos:
- Es necesario
Legacy View
descargar para tener permiso completo para operar el almacenamiento. - Se puede acceder a los archivos de aplicaciones almacenados en áreas no públicas
SAF访问
seleccionando archivos de directorio a través de SAF, y los usuarios pueden optar por acceder a los archivos de aplicaciones.
- La aplicación puede colocar los archivos que deben guardarse:
Images
,Video
,Audio
en el directorio público correspondiente. Otros archivos se pueden colocarDownloads
debajo sin eliminarlos después de la desinstalación.
2.7.8 Consultas de MediaStore
Cuando se usa MediaStore
para realizar query
acciones, Projection
cuando se usa, Column Name
se debe MediaStore
definir en.
2.8 Permiso WRITE_MEDIA_STORAGE
2.8.1 Antecedentes
WRITE_MEDIA_STORAGE
Es un permiso muy poderoso que permite que la aplicación obtenga acceso a todos los dispositivos de almacenamiento. Permiso para acceder a todos los dispositivos de almacenamiento; esto solo se debe otorgar al Media Stack.
2.8.2 Impacto de la compatibilidad
En el sistema Android, se estipula que WRITE_MEDIA_STORAGE
se puede obtener el grupo de usuarios media_rw
:
- Para todos los dispositivos de almacenamiento extraíbles, como tarjetas T y discos U, cuando se montan en Android, las aplicaciones normales solo tienen permisos de lectura y no de escritura. Sólo las aplicaciones del grupo de usuarios media_rw pueden escribir en dispositivos de almacenamiento extraíbles.
- Para Scoped Storage en Android Q, puede usar este permiso para configurar la aplicación para que se ejecute en modo de compatibilidad.
- Android CTS realizará pruebas. Las aplicaciones que el usuario puede iniciar no pueden solicitar este permiso.
Para obtener más información, consulte "Android Bootcamp 2019 - Descripción general de privacidad.pdf".
2.8.3 Adaptación
Si la aplicación necesita acceder Media
a o 外置存储设备
, puede utilizar MediaStore
o Storage Access Framework(SAF)接口
.
3 Resumen
Bien, aquí hay un resumen:
App TargetSDK > 28 即 Android10(Q)及以上
Proyecto, Google ha restringido el modo de espacio aislado de almacenamiento. En el usoAndroid10(Q)以上的设备
recomendado私有目录data/data
, ya no se puede acceder directamente外部SD卡存储目录
. Si necesita usarlo , debe acceder a él外部SD卡存储目录
a través de la interfaz, y solo se puede acceder a él , como , , , , , etc. Todas las aplicaciones pueden acceder a estos directorios de almacenamiento de tarjetas SD externas (directorios públicos), por lo que no son muy seguros.SAF
MediaStore
特定的外部SD卡存储目录
Downloads
Documents
Pictures
DCIM
Movies
Music
Ringtones
App TargetSDK <= 28 即 Android10(Q)以下
El proyecto no tiene restricciones, siempre que tengaWRITE_MEDIA_STORAGE
permisosREAD_MEDIA_STORAGE
. Entonces, si realmente no desea cambiar el almacenamiento SD externo, cambie el targetSdk del proyecto a <=28.- En cuanto
SAF
al métodoMediaStore
de acceso a la interfaz外部SD卡存储特定目录
, consulte la descripción anterior o consulte la documentación oficial para obtener más detalles. Aquí hay una demostración deMediaStore
cómo usarlo para su referencia外部SD卡存储特定目录
. No olvide hacer clic en Estrella.