Android不同存儲方式與所需權限

首先,App在手機上保存文件或者緩存數據時,應該遵守以下幾點:

1、不要隨意佔用用戶的內置存儲。
2、不要隨意在SD卡上新建目錄,應該放置自己應用包名對應的擴展存儲目錄下,卸載App時可以被自動清除。
3、對佔用的磁盤空間有上限,並按照一定的策略進行清除。

一、手機存儲路徑

Android系統分爲內部存儲和外部存儲,內部存儲是手機系統自帶的存儲,一般空間都比較小,外部存儲一般是SD卡的存儲,空間一般都比較大,但不一定可用或者剩餘空間可能不足。一般我們存儲內容都會放在外部存儲空間裏。使用過程注意事項:

  • 先判斷SD卡是否可用,可用時優先使用SD卡的存儲,不可用時用內部存儲
  • 存儲在SD卡上時,可以在SD卡上新建任意一個目錄存放,也可以存放在應用程序內部文件夾,區別是在SD卡的任意目錄存放時內容不會隨應用程序的卸載而消失,而在應用程序內部的內容會隨應用程序卸載消失。
  • 一般緩存文件放在應用程序內部,用戶主動保存的文件放在SD卡上的文件夾裏。如果在SD卡上任意新建目錄存放所有數據,用戶卸載時會殘存大量文件,招致用戶反感。

一般我們可以通過 Context 和 Environment 相關的方法獲取文件存取的路徑。

1、內部存儲

1)根目錄
Environment.getDataDirectory(): /data

2)獲取方式:

  • Context.getFileDir():獲取內置存儲下的文件目錄,可以用來保存不能公開給其他應用的一些敏感數據如用戶個人信息
  • Context.getCacheDir():獲取內置存儲下的緩存目錄,可以用來保存一些緩存文件如圖片,當內置存儲的空間不足時將系統自動被清除

3)絕對路徑

Environment.getDataDirectory(): /data //根目錄
Context.getCacheDir(): /data/data/應用包名/cache
Context.getFilesDir(): /data/data/應用包名/files
Context.getFileStreamPath(""): /data/data/應用包名/files
Context.getFileStreamPath(“name”): /data/data/應用包名/files/name //name爲子文件名

4)寫權限:不需要申請

5)這是手機的內置存儲,沒有root的過的手機是無法用文件管理器之類的工具查看的。而且這些數據也會隨着用戶卸載App而被一起刪除。這兩個目錄其實就對應着:
設置->應用->你的App->存儲空間 下面的清除數據和清楚緩存。

2、外部存儲1——應用擴展存儲

1)根目錄
Environment.getExternalStorageDirectory(): /storage/emulated/0

2)獲取方式:

  • Context.getExternalFilesDir():獲取SD卡上的文件目錄
  • Context.getExternalCacheDir():獲取SD卡上的緩存目錄

3)絕對路徑

Environment.getExternalStorageDirectory(): /storage/emulated/0 //根目錄
Context.getExternalCacheDir(): /storage/emulated/0/Android/data/com.learn.test/cache
Context.getExternalFilesDir(""): /storage/emulated/0/Android/data/com.learn.test/files
Context.getExternalFilesDir(“test”): /storage/emulated/0/Android/data/com.learn.test/files/test
Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES): /storage/emulated/0/Android/data/com.learn.test/files/Pictures //type爲文件類型

4)寫權限:

<uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE” />
<uses-permission android:name=“android.permission.READ_EXTERNAL_STORAGE” />

API < 19:需要申請
API >= 19:不需要申請

從 API 19 / Andorid 4.4 / KITKAT 開始,不再需要顯式聲明這兩個權限,除非要讀寫其他應用的應用數據($appDataDir)

5)既然是SD卡上的目錄,那麼是可以被其他的應用讀取到的,所以這個目錄下,不應該存放用戶的敏感信息。同上面一樣的,這裏的文件會隨着App卸載而被刪除,也可以由用戶手動在設置界面裏面清除。

3、外部存儲2——公共存儲目錄

1)獲取方式

  • Environment.getExternalStorageDirectory()

2)絕對路徑:SDCard/你設置的文件夾名字/

Environment.getExternalStorageDirectory(): /storage/emulated/0
Environment.getExternalStoragePublicDirectory(""): /storage/emulated/0
Environment.getExternalStoragePublicDirectory(“test”): /storage/emulated/0/test
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES): /storage/emulated/0/Pictures

3)寫權限:需要申請

4)這些目錄的內容不會隨着應用的卸載而消失,如果我們的App需要存儲一些公共的文件,甚至希望下載下來的文件即使在我們的App被刪除之後,還可以被其他App使用,那麼就可以使用這個目錄。這個目錄是始終需要申請SD寫入權限的。

我們可以在外部存儲上新建任意文件夾,不過在6.0及之後的系統需要動態申請權限

二、其他總結

1、基本操作

1)判斷sd卡是否可用

/**
 * Check if the primary "external" storage device is available.
 */
public static boolean hasSDCardMounted() {
    String state = Environment.getExternalStorageState();
    if (state != null && state.equals(Environment.MEDIA_MOUNTED)) {
        return true;
    } else {
        return false;
    }
}

2)Android6.0以上動態申請權限

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String[] permissions = {
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            };
            if (checkPermission(act)) {
                act.requestPermissions(permissions, REQUEST_CODE_PERMISSION_STORAGE);
        } 
        
private static boolean checkPermission(Activity act) {
        return ((act.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
                || (act.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED));


3)一個完整的判斷參考

if ((Build.VERSION.SDK_INT >= 19 || PermissionChecker.checkCallingOrSelfPermission(activity,
                        "android.permission.WRITE_EXTERNAL_STORAGE") == PermissionChecker.PERMISSION_GRANTED)
                        && Environment.getExternalStorageState().equals("mounted")
                        && external != null) {
                    sdCardPath = external.getAbsolutePath();
                }
2、Android6.0下應該把文件放到哪裏

如果僅僅是做了簡單的圖片緩存工作,那麼我們應該把圖片緩存放到/data/data/應用包名/cache/或者SDCard/Android/data/應用包名/cache/,因爲在6.0系統(API > 23)時,不需要申請權限就可以向這兩個目錄寫入文件。而且/data/data/應用包名/cache/目錄,是內置存儲的應用私有緩存目錄,在系統空間不夠時還會被自動清除,對於圖片緩存來講也是一個不錯的管理策略,不過谷歌建議我們最好還是自己實現緩存清除管理,例如用DiskLruCache。

實際上我們可以在API >= 19(不一定非要大於23)時,就可以在不需要申請權限的情況下把文件放到這兩個目錄了。如果開發的時候足夠規範,即使在API < 19時,我們申請到寫入權限後,我們也應該手動創建和前面相同的目錄,使得應用存儲數據目錄統一化。

3、兼容API < 19

爲了兼容API < 19,會在AndroidManifest.xml文件裏註冊WRITE_EXTERNAL_STORAGE,這時當App運行在一臺6.0的設備時,即使你的App全程都沒有調用requestPermissons來申請權限,用戶還是可以在Android6.0系統上 進入設置->應用->你的App->權限裏面,取消存儲空間這一個權限。記住是運行在6.0系統的機器上,這是關鍵,因爲低於6.0的系統根本沒有這個設置。只要在manifest裏面註冊了,就可以動態取消之,此時你的圖片在6.0機器上也就沒法緩存嘍。

解決:

將AndroidManifest.xml文件中的

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

改爲

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>

表示只在API <= 18時,才申請WRITE_EXTERNAL_STORAGE權限。這樣用戶就無法在Android6.0系統的設置下面看到存儲空間權限的開關,當然也就無法關閉它了。

3、如果希望存儲的內容可以被其他應用訪問 不要用內部存儲
4、分享存儲文件時 Android N(7.0)注意

對於面向 Android 7.0 的應用,Android 框架執行的 StrictMode API 政策禁止在您的應用外部公開 file:// URI。如果一項包含文件 URI 的 intent 離開您的應用,則應用出現故障,並出現 FileUriExposedException 異常。
要在應用間共享文件,您應發送一項 content:// URI,並授予 URI 臨時訪問權限。進行此授權的最簡單方式是使用 FileProvider 類。

private static Uri getFileUri(Context context, File file, Intent intent) {
        Uri uri;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            uri = Uri.fromFile(file);
        } else {
            uri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }
        return uri;
    }

參考:
數據和文件存儲概覽

將文件保存到外部存儲

Android存儲及getCacheDir()、getFilesDir()、getExternalFilesDir()、getExternalCacheDir()區別

Android6.0權限適配之WRITE_EXTERNAL_STORAGE(SD卡寫入)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章