1. Información general
Android 4.4(API 级别 19)
introducido 存储访问框架 (Storage Access Framework)
. SAF
Permite a los usuarios navegar y abrir fácilmente en todos sus proveedores de almacenamiento de documentos preferidos 文档
, 图像
y 其他文件
. UI
Los usuarios pueden explorar archivos y acceder a archivos usados recientemente de manera consistente en todas las aplicaciones y proveedores a través de estándares fáciles de usar .
El marco de acceso al almacenamiento SAF
incluye lo siguiente:
- Proveedor de documentos:
ConentProvider
una subclase que permite que un servicio de almacenamiento muestre los archivos que administra. Los proveedores de documentosDocumentsProvider
se implementan como subclases de la clase. La arquitectura del proveedor de documentos se basa en una jerarquía de archivos tradicional.Android
La plataforma incluye varios proveedores de documentos integrados,sd卡
correspondientes a las operacionesExternalStorageProvider
. - Aplicación cliente: Es nuestra aplicación habitual, llama
ACTION_OPEN_DOCUMENT
a estos tres métodos para abrir, crear documentos y abrir el árbol de documentosACTION_CREATE_DOCUMENT
.ACTION_OPEN_DOCUMENT_TREE
Intent
Action
- Selector: una interfaz de usuario del sistema, como la llamamos
DocumentUi
, que permite a los usuarios acceder a todos los documentos dentro de un proveedor de documentos que cumplen con los criterios de búsqueda de la aplicación cliente. NoDocumentUI
tiene íconos ni entradas en el escritorio y solo se puede acceder a través de lo anteriorIntent
.
En el marco SAF, no existe una interacción directa entre nuestra aplicación y DocumentProvider, sino a través de DocumentUi.
2 Uso del marco SAF
Como se mencionó anteriormente, SAF
el uso del marco es DocumentUI
indirecto a través del selector y las operaciones de archivos no se pueden realizar directamente.
Cómo usarlo:
2.1 Abrir archivo
private static final int READ_REQUEST_CODE = 42;
...
public void performFileSearch() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
//过滤器只显示可以打开的结果
intent.addCategory(Intent.CATEGORY_OPENABLE);
//要搜索通过已安装的存储提供商提供的所有文档
//intent.setType("*/*");
startActivityForResult(intent, READ_REQUEST_CODE);
}
@Override
public void onActivityResult(int requestCode, int resultCode,Intent resultData) {
//使用resultdata.getdata ( )提取该URI
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
Log.i(TAG, "Uri: " + uri.toString());
showImage(uri);
}
}
}
Uri de regreso:
content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera%2FIMG20190607162534.jpg
2.2 Abrir el árbol de archivos
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, OPEN_TREE_CODE);
private void handleTreeAction(Intent data){
Uri treeUri = data.getData();
//授予打开的文档树永久性的读写权限
final int takeFlags = intent.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uri, takeFlags);
//使用DocumentFile构建一个根文档,之后的操作可以在该文档上进行
mRoot = DocumentFile.fromTreeUri(this, treeUri);
//显示结果toast
showToast(" open tree uri "+treeUri);
}
Uri devuelto:
content://com.android.externalstorage.documents/tree/primary%3AColorOS
- Para el árbol de documentos que abrimos, el sistema nos otorgará permisos de lectura y escritura para todos los documentos bajo el árbol de documentos, por lo que podemos usar libremente los flujos o archivos de entrada y salida que presentamos anteriormente para leer y escribir, y esta autorización siempre será retenido al usuario para reiniciar el dispositivo.
- Pero a veces, necesitamos poder acceder permanentemente a estos archivos sin volver a autorizarnos después de reiniciar, por lo que usamos el método takePersistableUriPermission para conservar la autorización del sistema para nuestra uri, incluso si se reinicia el dispositivo.
- Es posible que hayamos guardado los URI a los que una aplicación accedió recientemente, pero es posible que ya no sean válidos; es posible que otra aplicación haya eliminado o modificado el documento. Por lo tanto, se debe llamar a getContentResolver().takePersistableUriPermission() para verificar los datos más recientes.
- Después de obtener el uri del directorio raíz, podemos usar la clase auxiliar DocumentFile para crear, eliminar archivos y otras operaciones fácilmente.
2.3 Crear archivo ACTION_CREATE_DOCUMENT
private void createDocument(){
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
//设置创建的文件是可打开的
intent.addCategory(Intent.CATEGORY_OPENABLE);
//设置创建的文件的minitype为文本类型
intent.setType("text/*");
//设置创建文件的名称,注意SAF中使用minitype而不是文件的后缀名来判断文件类型。
intent.putExtra(Intent.EXTRA_TITLE, "123.txt");
startActivityForResult(intent,CREATE_DOCUMENT_CODE);
}
private void handleCreateDocumentAction(Intent data){
if (data == null) {
return;
}
BufferedWriter bw = null;
try {
OutputStream os = getContentResolver().openOutputStream(uri);
bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write(" i am a text ");
showToast(" create document succeed uri "+uri);
} catch (IOException e) {
e.printStackTrace();
}finally {
closeSafe(bw);
}
}
2.4 Editar documentos
Después onActivityResult()
de obtener el Uri, puede operar el Uri:
private void alterDocument(Uri uri) {
try {
ParcelFileDescriptor pfd = getContext().getContentResolver().openFileDescriptor(uri, "w");
FileOutputStream fileOutputStream =
new FileOutputStream(pfd.getFileDescriptor());
fileOutputStream.write(("Overwritten by MyCloud at " + System.currentTimeMillis() + "\n").getBytes());
// Let the document provider know you' re done by closing the stream.fileOutputStream.close()
fileOutputStream.close();
pfd.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2.5 Eliminar documentos
Si obtiene el documento y el paquete URI
del documento , puede eliminar el documento. Por ejemplo:Document.COLUMN_FLAGS
SUPPORTS_DELETE
DocumentsContract.deleteDocument(getContentResolver(), uri);
2.6 Uso de la clase DocumentFile
DocumentFile
Es una clase de ayuda introducida google
para facilitar que todos utilicen y realicen operaciones con archivos. SAF
Su clase es relativamente cercana a la clase api
y está más en línea con los hábitos de los usuarios comunes, y su esencia interna utiliza métodos de clase para operar archivos. En otras palabras, también podemos usarlo para completar las operaciones de archivos proporcionadas por el marco sin usarlo en absoluto. Se proporcionan tres métodos de fábrica estáticos para crearlo.java
File
DocumentsContact
DocumentFile
DocumentsContact
SAF
DocumentFile
fromSingleUri
Este método necesita pasar un SAF
uri devuelto que apunte a un único archivo uri
. Nuestro uri devuelto es de este tipo y su clase de implementación correspondiente representa un único archivo. , este método pasa la uri que apunta a la carpeta, lo que devolvemos es este tipo, y su clase de implementación correspondiente es , que representa una carpeta. , este método pasa por una clase ordinaria, que es una simulación de la clase.ACTION_OPEN_DOCUMENT
ACTION_CREATE_DOCUMENT
ingleDocumentFile
fromTreeUri
ACTION_OPEN_TREE
TreeDocumentFile
fromFile
File
file
DocumentFile
El método se resume de la siguiente manera:
3 principios marco del SAF
SAF
El diagrama de clases del marco 3.1 es el siguiente:
Se puede ver en el diagrama de relación de clases que DocumentFile
la clase de herramienta finalmente DocumentsContract
implementa la operación y DocumentsContract
la operación final Provider
es DocumentsProvider
. DocumentsProvider
Hay tres categorías:
ExternalStorageProvider
Es para SD卡
uso externo Provider
y DownloadStorageProvider
para descarga Provider
.
ExternalStorageProvider
:com.android.externalstorage.documents
DownloadStorageProvider
:com.android.providers.downloads.documents
MediaDocumentProvider
:com.android.providers.media.documents
El siguiente es un análisis detallado del proceso de creación, modificación y eliminación de archivos,
se puede ver que DocumentFile
la clase auxiliar finalmente se DocumentsContract
opera a través deDocumentsProvider
Echemos un vistazo al proceso de salto a la selección PickerUI
:
PickerUI
Finalmente, también se ajustó DocumentsContract
al medio.
3.2 Formulario de organización de documentos en DocumentProvider
Dentro del proveedor de documentos, la estructura de datos sigue una jerarquía de archivos tradicional, como se muestra en la siguiente figura:
- Cada DocumentProvider puede tener uno o más directorios raíz como árboles de estructura de documentos. Cada directorio raíz tiene un COLUMN_ROOT_ID único y apunta al documento que representa el contenido del directorio raíz.
- Hay un documento debajo de cada directorio raíz, que apunta a 1 a n documentos, y cada uno de los documentos puede apuntar a 1 a N documentos, formando así una estructura de documentos en forma de árbol.
- Cada documento tendrá un COLUMN_DOCUMENT_ID único para hacer referencia a ellos. El ID del documento es único y no se puede cambiar una vez emitido porque se utiliza para la autorización URI permanente en todos los reinicios del dispositivo.
- El documento puede ser un archivo que se puede abrir (con un tipo MIME específico) o un directorio que contiene documentos adjuntos (con el tipo MIME MIME_TYPE_DIR).
- Cada documento puede tener diferentes funciones, como se describe en COLUMN_FLAGS. Por ejemplo, FLAG_SUPPORTS_WRITE, FLAG_SUPPORTS_DELETE y FLAG_SUPPORTS_THUMBNAIL. Varios directorios pueden contener el mismo COLUMN_DOCUMENT_ID.
Documento:
3.3 Proveedor de documentos personalizado
Si desea que los datos de su aplicación se documentsui
abran en , debe escribir los suyos propios document provider
. (Si es solo una operación de archivo normal, no es necesario definirla de esta manera)
1) Primero, debe declarar una personalizada en el Manifiesto provider
:
2) DocumentProvider
Interfaz básica implementada
4 Resumen del marco SAF
1. SAF
El marco no trata directamente con DocumentProvider
, sino DocumentUI
que opera indirectamente a través de.
2. Ya sea a través Intent
del método o a través de la clase auxiliar DocumentFile
para realizar operaciones con archivos, es necesario obtenerlo uri
, que solo se puede devolver uri
a través del método, por lo que no es muy conveniente. DocumentUI
Si puede aceptar DocumentUI
la interacción a través de él, el uso SAF
de un marco básicamente puede reemplazar el método de operación del archivo original.
Este capítulo le brinda una comprensión general SAF框架
y presentaremos el Android Q
modo sandbox en el próximo capítulo.(Scoped Storage)