Android_存儲訪問框架SAF


概念

存儲訪問框架---Storage Access Framework (SAF),這是在Android4.4(API level 19)之后引入的。

借助 SAF,用戶可輕松在其所有首選文檔存儲提供程序中瀏覽並打開文檔、圖像及其他文件。用戶可通過易用的標准界面,以統一方式在所有應用和提供程序中瀏覽文件,以及訪問最近使用的文件。

雲存儲服務或本地存儲服務可實現封裝其服務的 DocumentsProvider,進而參與此生態系統。只需幾行代碼,便可將需要訪問提供程序文檔的客戶端應用與 SAF 進行集成。

 

SAF 包含以下3部分內容:

文檔提供程序(Document provider):一個Content Provider, 以 DocumentsProvider 類的子類形式實現。文檔提供程序的架構基於傳統的文件層次結構,但其實際的數據存儲方式由您決定。Android 平台包含若干內置文檔提供程序,如 Downloads、Images 和 Videos。文檔提供程序 可讓存儲服務(如 Google Drive)顯示其管理的文件。

客戶端應用(Client app) :一種自定義應用,它會調用 ACTION_OPEN_DOCUMENT 和/或 ACTION_CREATE_DOCUMENT Intent 並接收文檔提供程序返回的文件。

選擇器(Picker) :一種系統界面,可讓用戶訪問所有滿足客戶端應用搜索條件的文檔提供程序內的文檔。

:在控制流部分和最后的編寫客戶端應用的例子中 有更清晰明確的介紹。控制流中的圖就包含了這3個內容。

 

控制流

如圖,SAF包含3個部分,文檔提供程序、客戶端應用和選擇器。上圖左側是照片應用(客戶端應用),中間是選擇器,右側是注冊的提供程序。

當應用(photo app)啟動Intent  ACTION_OPEN_DOCUMENT 或 ACTION_CREATE_DOCUMENT后,選擇器會前往每個已注冊的提供程序 並顯示匹配的Root目錄給用戶。選擇器為用戶提供了標准的文檔訪問界面(即使底層文檔提供程序與其差異比較大)。

如下圖就是一個選擇器,該圖還顯示可供客戶端應用使用的所有根目錄。

 

 

文檔提供程序

SAF 所圍繞的Content Provider是 DocumentsProvider 類的一個子類。在文檔提供程序內,數據結構采用傳統的文件層次結構:

 文檔提供程序數據模型。Root節點指向單個文檔,然后引出整個結構樹。

注意:

1.每個文檔提供程序有一個或多個Root節點(引出整個文檔結構樹的起點),且每個Root節點有唯一的COLUMN_ROOT_ID(DocumentsContract.Root)。Root設計是動態的,用以支持多賬號、Usb存儲或用戶注銷登錄等。

2.Root下只有一個文檔,這個文檔后指向1~N個文檔,之后的同樣指向1~N個文檔。

3.每個存儲 后端后會有一個唯一的COLUMN_DOCUMENT_ID(DocumentsContract.Document),用來引用這個文檔或目錄。

4.文檔可以是可打開的文件(具有特定的 MIME類型)或包含附加文檔的目錄(具有 MIME_TYPE_DIR MIME 類型)。

5.如 COLUMN_FLAGS 所描述,每個文檔可擁有不同功能。例如,FLAG_SUPPORTS_WRITE、FLAG_SUPPORTS_DELETE 和 FLAG_SUPPORTS_THUMBNAIL。多個目錄中可包含相同的 COLUMN_DOCUMENT_ID。

  

編寫客戶端應用

在 Android 4.3 及更低版本中,如果您想讓應用從其他應用中檢索文件,則該應用必須調用 ACTION_PICK 或 ACTION_GET_CONTENT 等 Intent。然后,用戶必須選擇一個要從中選取文件的應用,並且所選應用必須提供用戶界面,以便用戶瀏覽和選取可用文件。

在 Android 4.4 及更高版本中,您還可選擇使用 ACTION_OPEN_DOCUMENT Intent,此 Intent 會顯示由系統控制的選擇器界面,以便用戶瀏覽其他應用提供的所有文件。借助此界面,用戶便可從任何受支持的應用中選取文件。

ACTION_OPEN_DOCUMENT 並非用於代替 ACTION_GET_CONTENT。您應根據應用需求選擇所使用的 Intent:

如果您只想讓應用讀取/導入數據,請使用 ACTION_GET_CONTENT。使用此方法時,應用會導入數據(如圖片文件)的副本。
如果您想讓應用獲得對文檔提供程序所擁有文檔的長期、持續性訪問權限,請使用 ACTION_OPEN_DOCUMENT。例如,照片編輯應用可讓用戶編輯存儲在文檔提供程序中的圖像。

先看下下面代碼:

public class MainActivity extends Activity {

    private static final String TAG = "flx_saf";
    private static final int READ_REQUEST_CODE = 42;
    private static final int WRITE_REQUEST_CODE = 43;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
//        createFile();
//        fileSearch();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (data == null || resultCode != Activity.RESULT_OK) return;
        if (requestCode == READ_REQUEST_CODE) {
            Log.d( TAG, "READ_REQUEST_CODE uri : " + data.getData() );
            getPathForSearch( data.getData() );
        } else if(requestCode == WRITE_REQUEST_CODE) {
            Log.d( TAG, "WRITE_REQUEST_CODE uri : " + data.getData() );
        }
    }

   //only for Image Uri
private void getPathForSearch(Uri uri) { String[] selectionArgs = new String[] {DocumentsContract.getDocumentId(uri).split(":")[1]}; Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Images.Media._ID + "=?", selectionArgs, null ); if ( null != cursor ) { if ( cursor.moveToFirst() ) { int index = cursor.getColumnIndex( MediaStore.Images.Media.DATA ); if ( index > -1 ) { String path = cursor.getString( index ); Log.d( TAG, "onActivityResult path="+path+";id="+selectionArgs[0] ); } } cursor.close(); } } protected void fileSearch() { Intent intent = new Intent( Intent.ACTION_OPEN_DOCUMENT ); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/*"); startActivityForResult(intent, READ_REQUEST_CODE); } protected void createFile() { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/*"); intent.putExtra(Intent.EXTRA_TITLE, "test_create.png"); startActivityForResult(intent, WRITE_REQUEST_CODE); } }
  • 當執行fileSearch() 時,使用了ACTION_OPEN_DOCUMENT,intent啟動了選擇器,調試手機中調用的選擇器是com.android.documentsui,如圖:

 

選擇其中一個圖片,在onActivityResult()可以對結果進行處理。這里提取了選擇的文檔的Uri,有了Uri就可以對文檔進行更多操作,這里獲取了下文檔的路徑。

2019-10-09 10:35:02.112 2099-2099/com.flx.testsaf D/flx_saf: READ_REQUEST_CODE uri : content://com.android.providers.media.documents/document/image%3A333
2019-10-09 10:35:02.145 2099-2099/com.flx.testsaf D/flx_saf: onActivityResult path=/storage/emulated/0/screenshot2.png;id=333

注:這里只是驗證說明,getPathForSearch()這里的方法並未做兼容處理,這里的Uri是從Image文檔提供程序中傳入的Uri才有效,其他無法處理甚至報錯。

  •  當執行createFile(),使用ACTION_CREATE_DOCUMENT,啟動選擇器創建文檔test_create.png。也可以通過onActivityResult()對結果進行處理,獲取Uri進行更多操作。

不同文檔提供程序 保存,得到的Uri是不同的,如下是分別保存在Download和SD卡根目錄的Uri:

7266-7266/com.flx.testsaf D/flx_saf: WRITE_REQUEST_CODE uri : content://com.android.providers.downloads.documents/document/114
7266-7266/com.flx.testsaf D/flx_saf: WRITE_REQUEST_CODE uri : content://com.android.externalstorage.documents/document/primary%3Atest_create.png

1.這里只介紹了ACTION_CREATE_DOCUMENT和ACTION_OPEN_DOCUMENT使用,因為可以獲取操作文檔的Uri,更多操作可以查看文檔或者自己嘗試。

2.注意運行中的權限哦

 

 

官方文檔:https://developer.android.google.cn/guide/topics/providers/document-provider

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM