[Android Framework Series] Chapter 16 Storage Access Framework (SAF)

1 Overview

Android 4.4(API 级别 19)introduced 存储访问框架 (Storage Access Framework). SAFAllows users to easily browse and open in all their preferred document storage providers 文档, 图像and 其他文件. UIUsers can browse files and access recently used files in a consistent way across all apps and providers through easy-to-use standards .
Insert image description here
The storage access framework SAFincludes the following:

  1. Document Provider: ConentProviderA subclass that allows a storage service to display the files it manages. Document providers DocumentsProviderare implemented as subclasses of the class. The document provider's architecture is based on a traditional file hierarchy. AndroidThe platform includes several built-in document providers, sd卡corresponding to the operations ExternalStorageProvider.
  2. Client application: It is our usual app. It calls ACTION_OPEN_DOCUMENTthese three methods to open, create documents, and open the document tree ACTION_CREATE_DOCUMENT.ACTION_OPEN_DOCUMENT_TREEIntentAction
  3. Picker: A system UI, as we call it DocumentUi, that allows users to access all documents within a document provider that meet the client application's search criteria. This DocumentUIhas no desktop icons and entrances and can only be accessed through the above Intent.
    In the SAF framework, there is no direct interaction between our app application and DocumentProvider, but through DocumentUi.

2 Use of SAF framework

As mentioned above, SAFthe use of the framework is DocumentUIindirect through the selector, and file operations cannot be performed directly.
How to use it:

2.1 Open file

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);
	        }
	}
}

Return Uri:

content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera%2FIMG20190607162534.jpg

Insert image description here

2.2 Open the file tree

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);
}

Returned Uri:

content://com.android.externalstorage.documents/tree/primary%3AColorOS

Insert image description here

  1. For the document tree we open, the system will grant us read and write permissions for all documents under the document tree, so we can freely use the input and output streams or files we introduced above to read and write, and this authorization will always be retained. to the user to reboot the device.
  2. But sometimes, we need to be able to permanently access these files without re-authorization after restarting, so we use the takePersistableUriPermission method to retain the system's authorization for our uri, even if the device is restarted.
  3. We may have saved URIs that an app recently accessed, but they may no longer be valid — another app may have deleted or modified the document. Therefore, getContentResolver().takePersistableUriPermission() should be called to check for the latest data.
  4. After getting the uri of the root directory, we can use the DocumentFile auxiliary class to easily create, delete files and other operations.

2.3 Create file 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 Edit documents

After onActivityResult()obtaining the Uri, you can operate on the 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 Delete documents

If you obtain the document URIand the document's Document.COLUMN_FLAGSpackage SUPPORTS_DELETE, you can delete the document. For example:

DocumentsContract.deleteDocument(getContentResolver(), uri);

2.6 Use of DocumentFile class

DocumentFileIt is a help class introduced googleto facilitate everyone to use and perform file operations. SAFIts class is relatively close to the class apiand is more in line with the habits of ordinary users, and its internal essence uses class methods to operate files. In other words, we can also use it to complete the file operations provided by the framework without using it at all. Three static factory methods are provided to create itself.javaFileDocumentsContactDocumentFileDocumentsContactSAFDocumentFile

fromSingleUri,This method needs to pass in a SAFreturned uri that points to a , single file uri. Our uri returned is of this type, and its corresponding ,implementation class is, representing a single file. , this method passes in the uri pointing to the folder, what we return is this type, and its corresponding implementation class is , which represents a folder. , this method passes in an ordinary class, which is a simulation of the class.ACTION_OPEN_DOCUMENTACTION_CREATE_DOCUMENTingleDocumentFile
fromTreeUriACTION_OPEN_TREETreeDocumentFile
fromFileFilefile

DocumentFileThe method is summarized as follows:
Insert image description here

3 SAF framework principles

SAFThe class diagram of the 3.1 framework is as follows:

Insert image description here

It can be seen from the class relationship diagram that DocumentFilethe tool class ultimately DocumentsContractimplements the operation, and DocumentsContractthe final operation Provideris DocumentsProvider. DocumentsProviderThere are three categories:

ExternalStorageProviderIt is for external SD卡use Providerand DownloadStorageProviderfor downloading Provider.

ExternalStorageProvider:com.android.externalstorage.documents
DownloadStorageProvider:com.android.providers.downloads.documents
MediaDocumentProvider:com.android.providers.media.documents

The following is a detailed analysis of the process of creating, modifying, and deleting files.
It can be seen that DocumentFilethe auxiliary class is ultimately DocumentsContractoperated throughDocumentsProvider
Insert image description here

Let’s take a look at the process of jumping to selection PickerUI:
PickerUIFinally, it was also adjusted DocumentsContractto the middle.
Insert image description here

3.2 Document organization form in DocumentProvider

Within the document provider, the data structure follows a traditional file hierarchy, as shown in the following figure:
Insert image description here

  1. Each DocumentProvider may have one or more Root directories as document structure trees. Each root directory has a unique COLUMN_ROOT_ID and points to the document representing the content in the root directory.
  2. There is a document under each root directory, which points to 1 to n documents, and each of the documents can point to 1 to N documents, thus forming a tree-shaped document structure.
  3. Each Document will have a unique COLUMN_DOCUMENT_ID to reference them. The document ID is unique and cannot be changed once issued because they are used for permanent URI authorization across all device restarts.
  4. The document can be an openable file (with a specific MIME type) or a directory containing attached documents (with the MIME_TYPE_DIR MIME type).
  5. Each document can have different functions, as described by COLUMN_FLAGS. For example, FLAG_SUPPORTS_WRITE, FLAG_SUPPORTS_DELETE, and FLAG_SUPPORTS_THUMBNAIL. Multiple directories can contain the same COLUMN_DOCUMENT_ID.
    Document:
    Insert image description here

3.3 Custom DocumentProvider

If you want your application's data to be documentsuiopened in , you need to write your own document provider. (If it is just a normal file operation, there is no need to define it this way)
1) First, you need to declare a custom one in the Manifest provider:
Insert image description here

2) DocumentProviderBasic interface implemented
Insert image description here
Insert image description here

4 SAF Framework Summary

1. SAFThe framework does not deal directly with DocumentProvider, but DocumentUIoperates indirectly through.
2. Whether it is through Intentthe method or through the auxiliary class DocumentFileto perform file operations, it needs to be obtained uri. This can only be returned urithrough the method, so it is not very convenient. DocumentUIIf you can accept DocumentUIinteraction through it, using SAFa framework can basically replace the original file operation method.

This chapter gives you a general understanding SAF框架, and we will introduce Android Qthe sandbox mode in the next chapter.(Scoped Storage)

Guess you like

Origin blog.csdn.net/u010687761/article/details/133128538