[Serie Android Framework] Capítulo 16 Marco de acceso al almacenamiento (SAF)

1. Información general

Android 4.4(API 级别 19)introducido 存储访问框架 (Storage Access Framework). SAFPermite a los usuarios navegar y abrir fácilmente en todos sus proveedores de almacenamiento de documentos preferidos 文档, 图像y 其他文件. UILos 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 .
Insertar descripción de la imagen aquí
El marco de acceso al almacenamiento SAFincluye lo siguiente:

  1. Proveedor de documentos: ConentProvideruna subclase que permite que un servicio de almacenamiento muestre los archivos que administra. Los proveedores de documentos DocumentsProviderse implementan como subclases de la clase. La arquitectura del proveedor de documentos se basa en una jerarquía de archivos tradicional. AndroidLa plataforma incluye varios proveedores de documentos integrados, sd卡correspondientes a las operaciones ExternalStorageProvider.
  2. Aplicación cliente: Es nuestra aplicación habitual, llama ACTION_OPEN_DOCUMENTa estos tres métodos para abrir, crear documentos y abrir el árbol de documentos ACTION_CREATE_DOCUMENT.ACTION_OPEN_DOCUMENT_TREEIntentAction
  3. 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. No DocumentUItiene íconos ni entradas en el escritorio y solo se puede acceder a través de lo anterior Intent.
    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, SAFel uso del marco es DocumentUIindirecto 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

Insertar descripción de la imagen aquí

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

Insertar descripción de la imagen aquí

  1. 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.
  2. 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.
  3. 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.
  4. 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 URIdel documento , puede eliminar el documento. Por ejemplo:Document.COLUMN_FLAGSSUPPORTS_DELETE

DocumentsContract.deleteDocument(getContentResolver(), uri);

2.6 Uso de la clase DocumentFile

DocumentFileEs una clase de ayuda introducida googlepara facilitar que todos utilicen y realicen operaciones con archivos. SAFSu clase es relativamente cercana a la clase apiy 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.javaFileDocumentsContactDocumentFileDocumentsContactSAFDocumentFile

fromSingleUriEste método necesita pasar un SAFuri 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_DOCUMENTACTION_CREATE_DOCUMENTingleDocumentFile
fromTreeUriACTION_OPEN_TREETreeDocumentFile
fromFileFilefile

DocumentFileEl método se resume de la siguiente manera:
Insertar descripción de la imagen aquí

3 principios marco del SAF

SAFEl diagrama de clases del marco 3.1 es el siguiente:

Insertar descripción de la imagen aquí

Se puede ver en el diagrama de relación de clases que DocumentFilela clase de herramienta finalmente DocumentsContractimplementa la operación y DocumentsContractla operación final Provideres DocumentsProvider. DocumentsProviderHay tres categorías:

ExternalStorageProviderEs para SD卡uso externo Providery DownloadStorageProviderpara 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 DocumentFilela clase auxiliar finalmente se DocumentsContractopera a través deDocumentsProvider
Insertar descripción de la imagen aquí

Echemos un vistazo al proceso de salto a la selección PickerUI:
PickerUIFinalmente, también se ajustó DocumentsContractal medio.
Insertar descripción de la imagen aquí

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:
Insertar descripción de la imagen aquí

  1. 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.
  2. 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.
  3. 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.
  4. 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).
  5. 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:
    Insertar descripción de la imagen aquí

3.3 Proveedor de documentos personalizado

Si desea que los datos de su aplicación se documentsuiabran 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:
Insertar descripción de la imagen aquí

2) DocumentProviderInterfaz básica implementada
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí

4 Resumen del marco SAF

1. SAFEl marco no trata directamente con DocumentProvider, sino DocumentUIque opera indirectamente a través de.
2. Ya sea a través Intentdel método o a través de la clase auxiliar DocumentFilepara realizar operaciones con archivos, es necesario obtenerlo uri, que solo se puede devolver uria través del método, por lo que no es muy conveniente. DocumentUISi puede aceptar DocumentUIla interacción a través de él, el uso SAFde 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 Qmodo sandbox en el próximo capítulo.(Scoped Storage)

Supongo que te gusta

Origin blog.csdn.net/u010687761/article/details/133128538
Recomendado
Clasificación