AndroidQ(10)分割槽儲存完美適配方法
前言
最近時間在做AndroidQ的適配,截止到今天AndroidQ分割槽儲存適配完成,期間出現很多坑,目前網上的帖子大部分都是概述變更內容,接下來的幾篇帖子都是對分割槽儲存實際經驗程式碼總結,填坑經驗,特此記錄一下,也為大家提供幫助。
本篇主要是對AndroidQ(10)分割槽儲存適配具體實現
- 要點:
- Android Q檔案儲存機制修改成了沙盒模式
- APP只能訪問自己目錄下的檔案和公共媒體檔案
- 對於AndroidQ以下,還是使用老的檔案儲存方式
這裡需要注意:在適配AndroidQ的時候還要相容Q系統版本以下的,使用SDK_VERSION區分
背景
儲存許可權
Android Q仍然使用READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE作為儲存相關執行時許可權,但現在即使獲取了這些許可權,訪問外部儲存也受到了限制,只能訪問自身目錄下的檔案和公共內體檔案。
外部儲存結構劃分
公有目錄:Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones等
地址:/storage/emulated/0/Downloads(Pictures)等
公有目錄下的檔案不會跟隨APP解除安裝而刪除。
APP私有目錄
地址:/storage/emulated/0/Android/data/包名/files
私有目錄存放app的私有檔案,會隨著App的解除安裝而刪除。
適配指導
AndroidQ中使用ContentResolver進行檔案的增刪改查
1、獲取(建立)自身目錄下的資料夾
獲取及建立,如果手機中沒有對應的資料夾,則系統會自動生成
//在自身目錄下建立apk資料夾 File apkFile = context.getExternalFilesDir("apk");
2、建立自身目錄下的檔案
生成需要下載的路徑,通過輸入輸出流讀取寫入
String apkFilePath = context.getExternalFilesDir("apk").getAbsolutePath(); File newFile = new File(apkFilePath + File.separator + "temp.apk"); OutputStream os = null; try { os = new FileOutputStream(newFile); if (os != null) { os.write("file is created".getBytes(StandardCharsets.UTF_8)); os.flush(); } } catch (IOException e) { } finally { try { if (os != null) { os.close(); } } catch (IOException e1) { } }
3、建立公共目錄下的資料夾
通過MediaStore.insert寫入
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { return null; } ContentResolver resolver = context.getContentResolver(); ContentValues values = new ContentValues(); values.put(MediaStore.Downloads.DISPLAY_NAME,fileName); values.put(MediaStore.Downloads.DESCRIPTION,fileName); //設定檔案型別 values.put(MediaStore.Downloads.MIME_TYPE,"application/vnd.android.package-archive"); //注意MediaStore.Downloads.RELATIVE_PATH需要targetVersion=29,//故該方法只可在Android10的手機上執行 values.put(MediaStore.Downloads.RELATIVE_PATH,"Download" + File.separator + "apk"); Uri external = MediaStore.Downloads.EXTERNAL_CONTENT_URI; Uri insertUri = resolver.insert(external,values); return insertUri;
4、公共目錄下的指定資料夾下建立檔案
結合上面程式碼,我們主要是在公共目錄下建立檔案或資料夾拿到本地路徑uri,不同的Uri,可以儲存到不同的公共目錄中。接下來使用輸入輸出流就可以寫入檔案
重點:AndroidQ中不支援file://型別訪問檔案,只能通過uri方式訪問
ContentResolver resolver = context.getContentResolver(); Uri insertUri = resolver.insert(external,values); if(insertUri == null) { return; } String mFilePath = insertUri.toString(); InputStream is = null; OutputStream os = null; try { os = resolver.openOutputStream(insertUri); if(os == null){ return; } int read; File sourceFile = new File(sourcePath); if (sourceFile.exists()) { // 檔案存在時 is = new FileInputStream(sourceFile); // 讀入原檔案 byte[] buffer = new byte[1024]; while ((read = is.read(buffer)) != -1) { os.write(buffer,read); } } } catch (Exception e) { e.printStackTrace(); }finally { try { if (is != null) { is.close(); } if (os != null) { os.close(); } } catch (IOException e) { e.printStackTrace(); } }
5、通過MediaStore讀取公共目錄下的檔案
ParcelFileDescriptor parcelFileDescriptor = null; FileDescriptor fileDescriptor = null; Bitmap tagBitmap = null; try { parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri,"r"); if (parcelFileDescriptor != null && parcelFileDescriptor.getFileDescriptor() != null) { fileDescriptor = parcelFileDescriptor.getFileDescriptor(); //轉換uri為bitmap型別 tagBitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (parcelFileDescriptor != null) { parcelFileDescriptor.close(); } } catch (IOException e) { } }
6、使用MediaStore刪除檔案
context.getContentResolver().delete(fileUri,null,null);
7、APP通過MediaStore訪問檔案所需要的許可權
header 1 | 無許可權 | READ_EXTERNAL |
---|---|---|
Audio | 可讀寫APP自己建立的檔案,但不可直接使用路徑訪問 | 可以讀其他APP建立的媒體類檔案,刪改操作需要使用者授權 |
Image | 可讀寫APP自己建立的檔案,但不可直接使用路徑訪問 | 可以讀其他APP建立的媒體類檔案,刪改操作需要使用者授權 |
File | 可讀寫APP自己建立的檔案,但不可直接使用路徑訪問 | 不可讀寫其他APP建立的非媒體類檔案 |
Downloads | 可讀寫APP自己建立的檔案,但不可直接使用路徑訪問 | 不可讀寫其他APP建立的非媒體類檔案 |
後續對AndroidQ儲存針對具體功能做介紹,歡迎關注~
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。