Android存儲使用參考

可能遇到的問題

android系統(tǒng)自身自帶有存儲,另外也可以通過sd卡來擴充存儲空間。前者好比pc中的硬盤,后者好移動硬盤。 前者空間較小,后者空間大,但后者不一定可用。 開發(fā)應用,處理本地數(shù)據(jù)存取時,可能會遇到這些問題:

  1. 需要判斷sd卡是否可用: 占用過多機身內(nèi)部存儲,容易招致用戶反感,優(yōu)先將數(shù)據(jù)存放于sd卡;
  • 應用數(shù)據(jù)存放路徑,同其他應用應該保持一致,應用卸載時,清除數(shù)據(jù):

    • 標新立異在sd卡根目錄建一個目錄,招致用戶反感

    • 用戶卸載應用后,殘留目錄或者數(shù)據(jù)在用戶機器上,招致用戶反感

  • 需要判斷兩者的可用空間: sd卡存在時,可用空間反而小于機身內(nèi)部存儲,這時應該選用機身存儲;

  • 數(shù)據(jù)安全性,本應用數(shù)據(jù)不愿意被其他應用讀寫;

  • 圖片緩存等,不應該被掃描加入到用戶相冊等媒體庫中去。

基本操作

  1. 使用外部存儲,需要的權(quán)限,在AndoridManifest.xml中:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
> 從API 19 / Andorid 4.4 / KITKAT開始,不再需要顯式聲明這兩個權(quán)限,除非要讀寫其他應用的應用數(shù)據(jù)($appDataDir)
  • 判斷sd卡可用:
/**
 * Check if the primary "external" storage device is available.
 * 
 * @return
 */
public static boolean hasSDCardMounted() {
    String state = Environment.getExternalStorageState();
    if (state != null && state.equals(Environment.MEDIA_MOUNTED)) {
        return true;
    } else {
        return false;
    }
}

存儲的用量情況

  • 根據(jù)系統(tǒng)用戶不同,所能占用的存儲空間大小也有不同

    在API level 9及其以上時,File對象的getFreeSpace()方法獲取系統(tǒng)root用戶可用空間;
    getUsableSpace()取非root用戶可用空間

  • 當有多個存儲可用時獲取磁盤用量,根據(jù)當前系統(tǒng)情況選用合適的存儲。

  • 根據(jù)系統(tǒng)存儲用量,合理設定app所用的空間大??;運行時,也可做動態(tài)調(diào)整。

  • 在API level 9及其以上的系統(tǒng),可直接調(diào)用File對象的相關(guān)方法,以下需自行計算:

@TargetApi(VERSION_CODES.GINGERBREAD)
public static long getUsableSpace(File path) {
    if (path == null) {
        return -1;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
        return path.getUsableSpace();
    } else {
        if (!path.exists()) {
            return 0;
        } else {
            final StatFs stats = new StatFs(path.getPath());
            return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();
        }
    }
}

路徑的規(guī)律

一般地,通過Context 和Environment`相關(guān)的方法獲取文件存取的路徑。

通過這兩個類可獲取各種路徑,如圖:

    ($rootDir)
+- /data                -> Environment.getDataDirectory()
|   |
|   |   ($appDataDir)
|   +- data/com.srain.cube.sample
|       |
|       |   ($filesDir)
|       +- files            -> Context.getFilesDir() / Context.getFileStreamPath("")
|       |       |
|       |       +- file1    -> Context.getFileStreamPath("file1")
|       |   ($cacheDir)
|       +- cache            -> Context.getCacheDir()
|       |
|       +- app_$name        ->(Context.getDir(String name, int mode)
|
|   ($rootDir)
+- /storage/sdcard0     -> Environment.getExternalStorageDirectory()
    |                       / Environment.getExternalStoragePublicDirectory("")
    |
    +- dir1             -> Environment.getExternalStoragePublicDirectory("dir1")
    |
    |   ($appDataDir)
    +- Andorid/data/com.srain.cube.sample
        |
        |   ($filesDir)
        +- files        -> Context.getExternalFilesDir("")
        |   |
        |   +- file1    -> Context.getExternalFilesDir("file1")
        |   +- Music    -> Context.getExternalFilesDir(Environment.Music);
        |   +- Picture  -> ... Environment.Picture
        |   +- ...
        |
        |   ($cacheDir)
        +- cache        -> Context.getExternalCacheDir()
        |
        +- ???

各個路徑的特性

下面介紹這些路徑的特性以及使用中需要注意的細節(jié):

  1. 根目錄($rootDir):

    內(nèi)部存儲路徑: /data, 通過Environment.getDataDirectory() 獲取
    外部存儲路徑: /storage/sdcard0 (也有類似 /mnt/ 這樣的),通過Environment.getExternalStorageDirectory()獲取

    示例:

    Environment.getDataDirectory(): 
            /data
    
    Environment.getExternalStorageDirectory(): 
            /storage/sdcard0
    
  • 應用數(shù)據(jù)目錄($appDataDir)

    • 內(nèi)部儲存: $appDataDir = $rootDir/data/$packageName,
    • 外部存儲: $appDataDir = $rootDir/Andorid/data/$packageName
      在這些目錄下的數(shù)據(jù),在app卸載之后,會被系統(tǒng)刪除,我們應將應用的數(shù)據(jù)放于這兩個目錄中。
  • 外部存儲中,公開的數(shù)據(jù)目錄。 這些目錄將不會隨著應用的刪除而被系統(tǒng)刪除,請斟酌使用:

    Environment.getExternalStorageDirectory(): 
        /storage/sdcard0
    
    // 同 $rootDir
    Environment.getExternalStoragePublicDirectory(""): 
        /storage/sdcard0
    
    Environment.getExternalStoragePublicDirectory("folder1"): 
        /storage/sdcard0/folder1
    
  • 應用數(shù)據(jù)目錄下的目錄

    一般的在$appDataDir下,會有兩個目錄:

    1. 數(shù)據(jù)緩存:$cacheDir = $appDataDir/cache:

      • 內(nèi)部存儲:Context.getCacheDir(), 機身內(nèi)存不足時,文件會被刪除
      • 外部存儲:Context.getExternalCacheDir()

      外部存儲沒有實時監(jiān)控,當空間不足時,文件不會實時被刪除,可能返回空對象

      示例:

      Context.getCacheDir(): 
              /data/data/com.srain.cube.sample/cache
      
      Context.getExternalCacheDir(): 
              /storage/sdcard0/Android/data/com.srain.cube.sample/cache
      
    • 文件目錄 $filesDir = $appDataDir/files:

      • 內(nèi)部存儲:通過Context.getFilesDir() 獲取

      Context.getFileStreamPath(String name)返回以name為文件名的文件對象,name為空,則返回 $filesDir 本身

      示例:

      Context.getFilesDir(): 
              /data/data/com.srain.cube.sample/files
      
      Context.getFileStreamPath(""):
              /data/data/com.srain.cube.sample/files
      
      Context.getFileStreamPath("file1"):
              /data/data/com.srain.cube.sample/files/file1
      
      • 外部存儲:通過Context.getExternalFilesDir(String type), type為空字符串時獲取.

      type系統(tǒng)指定了幾種類型:

      Environment.DIRECTORY_MUSIC
      Environment.DIRECTORY_PICTURES
      ...
      

      示例:

      Context.getExternalFilesDir(""): 
              /storage/sdcard0/Android/data/com.srain.cube.sample/files
      
      Context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)
              /storage/sdcard0/Android/data/com.srain.cube.sample/files/Music
      
  • $cacheDir / $filesDir 安全性

在內(nèi)部存儲中,$cacheDir, $filesDir是app安全的,其他應用無法讀取本應用的數(shù)據(jù),而外部存儲則不是。

在外部存儲中,這兩個文件夾其他應用程序也可訪問。

在外部存儲中,$filesDir中的媒體文件,不會被當做媒體掃描出來,加到媒體庫中。

  • $cacheDir / $filesDir 同級目錄

在內(nèi)部存儲中:通過 Context.getDir(String name, int mode)可獲取和 $filesDir / $cacheDir 同級的目錄

目錄的命名規(guī)則為 app_ + name, 通過mode可控制此目錄為app私有還是其他app可讀寫。

示例:

Context.getDir("dir1", MODE_PRIVATE):
        Context.getDir: /data/data/com.srain.cube.sample/app_dir1
  • 特別注意, 對于外部存儲,獲取$cacheDir 或者 $filesDir及其下的路徑

在API level 8 以下,或者空間不足,相關(guān)的方法獲路徑為空時,需要自己構(gòu)造。

@TargetApi(VERSION_CODES.FROYO)
public static File getExternalCacheDir(Context context) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO)) {
        File path = context.getExternalCacheDir();

        // In some case, even the sd card is mounted,
        // getExternalCacheDir will return null
        // may be it is nearly full.
        if (path != null) {
            return path;
        }
    }

    // Before Froyo or the path is null,
    // we need to construct the external cache folder ourselves
    final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/";
    return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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