Android 文件存儲淺析

最近做的一個需求和文件存儲有關係。由於之前沒有系統梳理過,對文件存儲方面的知識一直很懵懂。趁着周末有時間,趕緊梳理一波。

這首從網上找到的一張圖,很好的概括了外部存儲和內部存儲。

 

下面我們再來具體介紹相關知識和內容。

內部存儲 

內部存儲,位於data/data/包名/路徑下

是否需要用戶權限:否

是否能被其他應用訪問:否

卸載應用數據是否被刪除:是

內部存儲控件不需要用戶權限,這意味着我們不需要用戶去授權下面的權限:

android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_EXTERNAL_STORAGE

對於設備中每一個安裝的 App,系統都會在 data/data 目錄下以應用程序包名自動創建與之對應的文件夾,可以直接讀寫該目錄下的文件。而且該目錄下的文件不能被其他應用訪問。這也就保證了我們應用內部存儲的文件的安全性和隱私性,如果我們需要查看自己應用內部的文件,我們可以通過 Android Studio的Device File Explore 工具進行訪問:

   

 

通過這個,可以查看對應應用的存儲文件。

/data/data/應用名/cache :存放的是APP的緩存信息

/data/data/應用名/code_cache :在運行時存放應用產生的編譯或者優化的代碼

/data/data/應用名/files : 存放APP的文件信息

還有一些運行時,產生的文件夾,例如調用 SharedPreference 所產生的 /data/data/應用包名/shared_prefs 目錄,存放着 app 的 SharedPreference 所產生的 xml 文件,還有調用數據庫所產生的 **/data/data/應用包名/databases/** 文件夾,這裡就不一一舉例。

從技術上來講如果你在創建內部存儲文件的時候將文件屬性設置成可讀,其他 app 能夠訪問自己應用的數據,前提是他知道你這個應用的包名,如果一個文件的屬性是私有(private),那麼即使知道包名其他應用也無法訪問。 內部存儲空間十分有限,因而顯得可貴,另外,它也是系統本身和系統應用程序主要的數據存儲所在地,一旦內部存儲空間耗盡,手機也就無法使用了。所以對於內部存儲空間,我們要盡量避免使用。Shared Preferences 和 SQLite 數據庫都是存儲在內部存儲空間上的。內部存儲一般用 Context 來獲取和操作。
訪問內部存儲的API方法:

  1. getFilesDir().getAbsolutePath()  : /data/user/0/com.example.myapplication/files

  2. getCacheDir().getAbsolutePath() :  /storage/emulated/0/Android/data/com.example.myapplication/cache

  3. getDir(「myFile」, MODE_PRIVATE).getAbsolutePath()  : /data/user/0/com.example.myapplication/app_myfile

  4. getCodeCacheDir().getAbsolutePath() : /data/user/0/com.example.myapplication/code_cache  ,要求Android5.0+

外部存儲

概念:最容易混淆的是外部存儲,因為老的 Android 系統的跟新的 Android 系統是有差別的,很多人去網上查找資料,看了一下以前的資料,又看了一下現在的資料,但是發現它們說法不一樣然後就困惑了。

首先說一個大家普遍的概念 “如果在 pc 機上是區分外部存儲和內部存儲的話,那麼電腦自帶的硬盤算是內部存儲,U盤或者移動硬盤就是外部存儲了。” 因此很多人帶着這樣的理解去看待安卓手機,把內置存儲(機身存儲)當做內部存儲,而把擴展的 SD 卡當做是外部存儲。

這麼認為確實沒錯,因為在 4.4(API19)以前的手機上確實是這樣的,手機自身帶的存儲卡就是內部存儲,而擴展的SD卡就是外部存儲。

但是從 4.4 的系統開始,很多的中高端機器都將自己的機身存儲擴展到了 8G 以上,比如有的人的手機是 16G 的,有的人的手機是 32G 的,但是這個 16G,32G 是內部存儲嗎,不是的,它們依然是外部存儲。

也就是說 4.4 系統及以上的手機將機身存儲存儲(手機自身帶的存儲叫做機身存儲)在概念上分成了 “內部存儲internal” 和 “外部存儲external” 兩部分。既然 16G,32G 是外部存儲,那有人又有疑惑了,那 4.4 系統及以上的手機要是插了 SD 卡呢,SD 卡又是什麼呢,如果 SD 卡也是外部存儲的話,那怎麼區分機身存儲的外部存儲跟 SD 卡的外部存儲呢?

對,SD卡也是外部存儲,那怎麼區分呢,在4.4以後的系統中,API提供了這樣一個方法來遍歷手機的外部存儲路徑:

File[] files;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    files = getExternalFilesDirs(Environment.MEDIA_MOUNTED);
    for(File file:files){
        Log.e("main",file);
    }
}

如果你的手機插了SD卡的話,那麼它打印的路徑就有兩條了,例如我的華為榮耀 7 插了SD卡,它的結果如下:

/storage/emulated/0/Android/data/packname/files/mounted
/storage/B3E4-1711/Android/data/packname/files/mounted

其中 /storage/emulated/0 目錄就是機身存儲的外部存儲路徑,而 /storage/B3E4-1711/ 就是 SD 卡的路徑,他們統稱為外部存儲。

一般對於外部存儲可以分為兩類,外部公有和外部私有。

外部公有

是否需要用戶權限:

是否能被其他應用訪問:

卸載應用數據是否被刪除:

公共目錄必須需要用戶授權讀寫的權限,這意味着我們需要在 AndroidManifest.xml 中註冊用戶權限。

    <!-- 外部存儲寫入數據權限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

並且在 Android 6.0 系統之後需要申請用戶權限,並獲得用戶授權,才能讀寫文件。

公共目錄相對開放,我們可以訪問其他APP存在公共目錄下的文件,並且當 APP 被刪除時,並不會刪除應用存在公共目錄下的文件。

我們可以通過 Environment 對象,訪問讀寫公共目錄的文件。

在對外部存儲進行讀寫的時候,應該先判斷一下外部存儲的狀態,是否能夠支持讀寫。

Environment.getExternalStorageState() 
/**       {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
 *        {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
 *        {@link #MEDIA_NOFS}, {@link #MEDIA_MOUNTED},
 *        {@link #MEDIA_MOUNTED_READ_ONLY}, {@link #MEDIA_SHARED},
 *        {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}
 */

只有在返回值為 MEDIA_MOUNTED 表示當前是可正常讀寫的。

接下來讓我們看看相關的API。

1. Environment.getExternalStorageDirectory() : /storage/emulated/0

2. Environment.getExternalStoragePublicDirectory(String type)  
       Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS).getAbsolutePath()  : /storage/emulated/0/Documents
    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath() : /storage/emulated/0/Music

外部私有

是否需要用戶權限:4.4以上不需要

是否能被其他應用訪問:否

卸載應用數據是否被刪除:是

私有目錄,在 Android4.4 系統以上。不需要註冊和用戶授權外部私有存儲的讀寫的權限,就可以在應用的外部私有進行讀寫文件。並且文件不能被其他應用所訪問,具有較好的隱私性和安全性,並且在用戶刪除的時候,對應的應用私有目錄也會被刪除。

私有目錄地址:/storage/emulated/0/Android/data/應用包名

相關API如下:

getExternalCacheDir().getAbsolutePath()  // /storage/emulated/0/Android/data/com.example.myapplication/cache

getExternalFilesDir("mytest").getAbsolutePath() // /storage/emulated/0/Android/data/com.example.myapplication/files/mytest
getExternalFilesDir(null).getAbsolutePath()  // /storage/emulated/0/Android/data/com.example.myapplication/files

總結

本文詳細介紹了 android 的外部存儲和私有存儲。大家在有保存文件的需求的時候,根據自己的需要,選擇到底是存在哪裡比較合適。內部存儲相對較小,不介意把一些大文件存在其中。應該存在外部存儲會更好。對於可以給其他文件訪問的,可以存在外部存儲的公有文件裏面。

 

參考文獻

Tags: