Android Q中文件沙盒模式讀寫文件 08-16

嘛~好久沒寫了

當(dāng)前Android Q中啟用了沙盒存儲(chǔ)模式,限制了APP向SDcard中讀寫文件,當(dāng)前谷歌尚未將該限制強(qiáng)制執(zhí)行,可以通過設(shè)置屬性啟用舊存儲(chǔ)特性。[原文]

沙盒模式下,每個(gè)APP在訪問sdcard時(shí)會(huì)進(jìn)入過濾視圖,只能訪問私有路徑(Context.getExternalFilesDir())和公共存儲(chǔ)空間(多媒體,MediaStore)。

官方文檔給對應(yīng)的解決方案,但是在我看來并不詳盡,目前可以看到有以下三個(gè)解決方案:

文件位置 所需權(quán)限 訪問方法 (*) 卸載應(yīng)用時(shí)是否移除文件? 是否需要權(quán)限
特定于應(yīng)用的目錄 getExternalFilesDir()
媒體集合 (照片、視頻、音頻) READ_EXTERNAL_STORAGE (僅當(dāng)訪問其他應(yīng)用的文件時(shí)) MediaStore
下載內(nèi)容 (文檔和 電子書籍) 存儲(chǔ)訪問框架 (加載系統(tǒng)的文件選擇器)

除了第一種寫在“/sdcard/Android/data/packageName/file”路徑中可以正常使用InputStream&OutputStream讀寫文件,另外兩種方法都無法直接對文件IO操作。
所以后續(xù)有向SDcard中寫文件的需求優(yōu)先向APP私有路徑中寫入。

但是,需要解決APP之間共享文件的場景,比如手機(jī)唯一標(biāo)識配置文件(UUID啥的),這種需求場景需要對MediaStore和SAF存儲(chǔ)訪問框架做調(diào)研。

一、存儲(chǔ)訪問框架

訪問存儲(chǔ)框架是一個(gè)基于intent發(fā)起的文件檢索UI,一般只能在View存在時(shí)調(diào)用系統(tǒng)的文件檢索框架,在startActivityForResult() 的回調(diào)中收到文件對象。

/**
   * Fires an intent to spin up the "file chooser" UI and select an image.
   * 視圖檢索文件
   */
  public void performFileSearch() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("*/*");
    startActivityForResult(intent, READ_REQUEST_CODE);
  }

但咱是寫SDK的,不能在FileManager單例或者靜態(tài)方法中讀文件的話,對咱毫無意義

二、MediaStore

MediaStore是外部存儲(chǔ)空間的公共媒體集合,存放的都是多媒體文件,在API >= 29后加入了download集合

一般Android中通過手機(jī)數(shù)據(jù)庫查詢Uri來對MediaStore中的文件做查詢,然后讀取文件,下面以MediaStore.Files查詢所有文件為例:

  private void testFiles() {
    ContentResolver contentResolver= this.getApplicationContext().getContentResolver();
    String [] photoColumns=new String[]{
        MediaStore.Files.FileColumns._ID,
        MediaStore.Files.FileColumns.DATA,
        MediaStore.Files.FileColumns.TITLE,
        MediaStore.Files.FileColumns.MIME_TYPE,
        MediaStore.Files.FileColumns.SIZE
    };
    Cursor cursor;
    cursor=contentResolver.query
        (MediaStore.Files.getContentUri("external"), photoColumns,
            null,
            null,
            null);
    while (cursor.moveToNext()) {
      String _id=
          cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID));
      String filePath=
          cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
      String title=
          cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.TITLE));
      String mime_type=
          cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MIME_TYPE));
      Log.d(TAG, "_id = " + _id);
      Log.d(TAG, "title = " + title);
      Log.d(TAG, "filePath = " + filePath);
      Log.d(TAG, "mime_type = " + mime_type);
    }
    Log.d(TAG, "Finish Cursor Search");
  }

通過上述代碼可以在所有文件中找到指定文件,和絕對路徑。同理,換成Image、Video、Audio、Downloads都一樣,只是在數(shù)據(jù)庫查詢時(shí)把Uri替換掉。

PS: 但是查詢有個(gè)致命傷,在開啟沙盒模式時(shí),只能在數(shù)據(jù)庫中查詢到Image、Video、Audio三個(gè)分類視圖中的文件,那么我SDK向SDcard中寫入的配置文件就不能在沙盒模式下通過數(shù)據(jù)庫查詢到。

只能獲取到mime_type == {“image/”, “video/”, “audio/*”}這三類文件

這可怎么辦呀

上邊代碼中通過ContentResolver.query查詢到文件詳情,但如果需要修改文件內(nèi)容,則使用ContentResolver.openFileDescriptor()獲取單個(gè)文件描述符。配置文件拿不到,先不管這個(gè)文件讀寫了。

小結(jié)

目前只能先使用官方支持的規(guī)避方案:通過設(shè)置android:requestLegacyExternalStorage="false"屬性來關(guān)閉APP的沙盒機(jī)制。
我還會(huì)回來的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容