Android存儲使用參考

轉自:https://www.liaohuqiu.net/cn/posts/storage-in-android/

Android存儲使用參考

15 Jan 2014


可能遇到的問題

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

  1. 需要判斷sd卡是否可用: 佔用過多機身內部存儲,容易招致用戶反感,優先將數據存放於sd卡;
  2. 應用數據存放路徑,同其他應用應該保持一致,應用卸載時,清除數據:

    • 標新立異在sd卡根目錄建一個目錄,招致用戶反感
    • 用戶卸載應用後,殘留目錄或者數據在用戶機器上,招致用戶反感
  3. 需要判斷兩者的可用空間: sd卡存在時,可用空間反而小於機身內部存儲,這時應該選用機身存儲;

  4. 數據安全性,本應用數據不願意被其他應用讀寫;

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


基本操作

  1. 使用外部存儲,需要的權限,在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開始,不再需要顯式聲明這兩個權限,除非要讀寫其他應用的應用數據($appDataDir)

  2. 判斷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;
        }
    }
    

存儲的用量情況

  • 根據系統用戶不同,所能佔用的存儲空間大小也有不同

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

    getUsableSpace()取非root用戶可用空間

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

  • 根據系統存儲用量,合理設定app所用的空間大小;運行時,也可做動態調整。

  • 在API level 9及其以上的系統,可直接調用File對象的相關方法,以下需自行計算:

    @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();
            }
        }
    }
    

路徑的規律

一般地,通過Context 和 Environment相關的方法獲取文件存取的路徑。

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

    ($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()
        |
        +- ???

各個路徑的特性

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

  1. 根目錄($rootDir):

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

      示例:

      Environment.getDataDirectory(): 
              /data
      
      Environment.getExternalStorageDirectory(): 
              /storage/sdcard0
      

  2. 應用數據目錄($appDataDir)

    • 內部儲存: $appDataDir = $rootDir/data/$packageName,
    • 外部存儲: $appDataDir = $rootDir/Andorid/data/$packageName

    在這些目錄下的數據,在app卸載之後,會被系統刪除,我們應將應用的數據放於這兩個目錄中。


  3. 外部存儲中,公開的數據目錄。 這些目錄將不會隨着應用的刪除而被系統刪除,請斟酌使用:

    Environment.getExternalStorageDirectory(): 
        /storage/sdcard0
    
    // 同 $rootDir
    Environment.getExternalStoragePublicDirectory(""): 
        /storage/sdcard0
    
    Environment.getExternalStoragePublicDirectory("folder1"): 
        /storage/sdcard0/folder1
    

  4. 應用數據目錄下的目錄

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

    1. 數據緩存:$cacheDir = $appDataDir/cache:

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

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

        示例:

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

      • 內部存儲:通過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系統指定了幾種類型:

        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
        

    3. $cacheDir / $filesDir 安全性

      在內部存儲中,$cacheDir$filesDir是app安全的,其他應用無法讀取本應用的數據,而外部存儲則不是。

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

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


    4. $cacheDir / $filesDir 同級目錄

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

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

      示例:

      Context.getDir("dir1", MODE_PRIVATE):
              Context.getDir: /data/data/com.srain.cube.sample/app_dir1
      

    5. 特別注意, 對於外部存儲,獲取$cacheDir 或者 $filesDir及其下的路徑

      在API level 8 以下,或者空間不足,相關的方法獲路徑爲空時,需要自己構造。

      @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);
      }
      


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