AndroidQ文件存儲適配

安卓Q即安卓10.0已經發布多時,不過大多數開發者並沒有真機去測試,最近各廠商系統陸續推送了10.0的升級,因此必須要考慮去適配10.0系統了(建議大家先查看安卓Q系統權限變更相關文章,這裏只說存儲權限的適配方法,不做詳細介紹)!

關於10.0系統權限方面的改變,大家可以搜索相關文章,這裏主要講一下存儲權限的變化,10.0之前我們在保存或者查詢文件時,首先需要申請存儲權限:

    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />

但是,在10.0(targetSdkVersion=29)系統中,該權限已經不再起作用。應用中的行爲表現爲,即便你開啓了存儲權限,當你檢測是否開啓時,返回的結果是未開啓,所以當你在把targetSdkVersion設爲29或更高時,就一定要考慮這個問題了!對於暫時不想適配的,又不影響應用運行的方法,其它文章也有介紹,比如:targetSdkVersion設置爲29以下,以及:

<application android:allowExternalStorageSandbox="false" ... >
  </application>

等,但這些方法都是暫時的,過後的版本,不論你怎麼設置,都無法再使用10.0以前的文件存儲方式了,也就是說你必須要適配安卓Q即10.0!

Q的存儲方式變化,即引入了沙盒機制,應用可以隨意訪問自身在沙盒內創建的文件夾及文件,不需要任何權限,且在沙盒內創建的文件夾及文件會隨着應用的卸載一併刪除。沙盒路徑爲:

內部存儲/Android/data/com.xx.xx(應用包名)/files/

當你爲應用創建沙盒文件時,可去這裏查看!

需要注意的是,沙盒裏的文件並不能對外顯示,比如Q以前,我們保存圖片後,去相冊裏查看,立馬就能看到剛剛保存的圖片,但在Q系統中,保存圖片到沙盒後,再去相冊中查看是看不到的,這裏需要一個騷操作,是保存到沙盒中後,需要再手動複製一份到公共文件夾中(公共文件夾包括:Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones 等),在公共文件夾中創建的文件在應用卸載時是不會被刪除的。

因此,我們在保存圖片,音視頻,其它文件時,需要對外顯示的,就可以複製一份到相應的公共文件夾,不需要對外顯示的就不用動,應用內部顯示自己保存的內容時,就直接訪問自己的沙盒中的目錄就可以了!

具體方法:
保存圖片:

String filepath=context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + "/" + 自己定義的文件夾名稱+ "/";
String filename="xxx.png";
File imageFile = new File(filepath, filename);//這一步,系統會自動爲你在沙盒中創建文件夾
...此處爲保存(下載)圖片的方法...

下載完成後,可以去上述文件夾路徑查看是否保存成功!若要對外顯示,則需要複製到公共文件夾:

    @RequiresApi(api = Build.VERSION_CODES.Q)
    public static void copyPrivateImgToCommen(Context context, String orgFilePath, String displayName) {
        ContentValues values = new ContentValues();
        values.put(MediaStore.Files.FileColumns.DISPLAY_NAME, displayName);
        values.put(MediaStore.Files.FileColumns.TITLE, displayName);
        values.put(MediaStore.Files.FileColumns.MIME_TYPE, "image/*");
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/" + 自己定義的文件夾名稱);
        Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        ContentResolver resolver = context.getContentResolver();
        Uri insertUri = resolver.insert(external, values);
        InputStream ist = null;
        OutputStream ost = null;
        try {
            ist = new FileInputStream(new File(orgFilePath));
            if (insertUri != null) {
                ost = resolver.openOutputStream(insertUri);
            }
            if (ost != null) {
                byte[] buffer = new byte[4096];
                int byteCount = 0;
                while ((byteCount = ist.read(buffer)) != -1) {
                    ost.write(buffer, 0, byteCount);
                }
            }
        } catch (IOException e) {

        } finally {
            try {
                if (ist != null) {
                    ist.close();
                }
                if (ost != null) {
                    ost.close();
                }
            } catch (IOException e) {

            }
        }
    }

此方法參考自其它開發者文章,主要在於這幾個字段:
MediaStore.Files.FileColumns.MIME_TYPE:文件類型,圖片即“image/”,視頻即“video/”,
MediaStore.Images.Media.RELATIVE_PATH:存儲路徑,圖片即MediaStore.Images,視頻即MediaStore.Video。
注意:即便是在自己的沙盒中,保存圖片和視頻等,也要根據文件類型,選擇對應的文件夾如:Environment.DIRECTORY_PICTURES或者Environment.DIRECTORY_MOVIES。

保存視頻:方法同保存圖片,但要注意區分文件類型及存儲路徑!

如何查詢自己保存的文件?以圖片爲例:

            File privateFile = getExternalFilesDir(Environment.DIRECTORY_PICTURES + "/自己定義的文件夾名稱");
            File[] files = privateFile.listFiles();
            if (files != null) {
                for (File file : files) {
                     //file 即你保存的圖片文件
                }
            }

其它類型,就取對應的Environment.DIRECTORY_路徑!

寫在最後,以上方法都是在Q系統下的操作方法,適配時需要判斷系統版本號,Build.VERSION.SDK_INT<Q時,依然要申請存儲權限,並按Q之前的操作方法去操作,>=Q時,則無需再申請權限,按Q操作方式操作即可!

補充:
安卓Q如何進行選擇相冊和拍照呢?又如何進行文件上傳操作呢?

如果只用來顯示,那麼只需要獲取到圖片/視頻Uri就可以了,但是如果要獲取到file進行上傳操作呢?如果你還是用Q以前的方法:File file=new File(path);的話你會發現,根本無法獲取到這個文件,我們只能通過曲線救國的方法,即拍照和選取相冊完成後,得到圖片uri,並通過該uri轉成file的形式,將file保存在沙盒文件夾內,然後再去沙盒文件夾內取file,再上傳!也就是需要把你要上傳的文件複製一份到沙盒…呵呵!

另外,拍照時調用相機方法有變化,需要判斷系統版本號,Q系統需要通過Uri形式獲取到拍照結果:

            ContentValues values = new ContentValues();
            values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
            values.put(MediaStore.Images.Media.MIME_TYPE, "image/*");
            values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
            uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

需要記錄這個Uri,拍照結果在onActivityResult中獲取:
Q系統通過url轉File的形式得到圖片路徑:

    public static String uriToFile(Context context, Uri uri, String privateFileName) {
        File privateFile = new File(getImagePathCache(context), privateFileName + ".png");
        File fileParent = privateFile.getParentFile();
        if (!fileParent.exists()) {
            fileParent.mkdirs();
        }
        if (privateFile.exists()) return privateFile.getAbsolutePath();
        InputStream ist = null;
        OutputStream ost = null;
        try {
            privateFile.createNewFile();
            ist = context.getContentResolver().openInputStream(uri);
            ost = new FileOutputStream(privateFile);
            byte[] buffer = new byte[4096];
            int byteCount = 0;
            while ((byteCount = ist.read(buffer)) != -1) {  // 循環從輸入流讀取 buffer字節
                ost.write(buffer, 0, byteCount);        // 將讀取的輸入流寫入到輸出流
            }
        } catch (IOException e) {
            return "";
        } finally {
            try {
                if (ist != null) {
                    ist.close();
                }
                if (ost != null) {
                    ost.close();
                }
            } catch (IOException e) {
                return "";
            }
        }
        return privateFile.getAbsolutePath();
    }

其中的getImagePathCache方法就是你的沙盒文件夾路徑:

context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + "/cache/";//cache是自定義的文件夾名稱

相冊選擇結果同樣在onActivityResult中獲取:
相冊選擇的結果可以直接通過onActivityResult方法返回的intent.getData()獲取,然後調用上述uri轉File方法,得到圖片路徑!

如果考慮這種緩存文件佔用內存空間,則在使用後刪除即可!

發佈了101 篇原創文章 · 獲贊 492 · 訪問量 44萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章