Android 文件保存

選擇內部或外部存儲

全部Android設備都有兩個文件存儲區:「內部」和「外部」存儲。這些名稱來自Android的早期,大多數設備提供內置的非易失性存儲器(內部存儲),以及可移動存儲介質,如microSD卡(外部存儲)。如今,許多設備將永久存儲空間劃分爲單獨的「內部」和「外部」分區。所以,即便沒有可移動存儲介質,這兩個存儲空間也始終存在,不管外部存儲是否可移動,API行爲都是相同的。android

因爲外部存儲多是可移除的,所以這兩個選項之間存在一些差別,以下所示。數組

內部存儲器:

  • 它始終可用。
  • 內部存儲器保存的文件只能由您的應用訪問。
  • 當用戶卸載您的應用程序時,系統會從內部存儲中刪除全部應用程序的文件。
  • 當您但願確保用戶和其餘應用程序都沒法訪問您的文件時,最好使用內部存儲。

外部存儲:

  • 外部存儲並不老是可用,由於用戶能夠將外部存儲裝載爲USB存儲器,並在某些狀況下將其從設備中移除。
  • 外部存儲是全局可讀的,所以保存在此處的文件可能會在您的控件以外讀取。
  • 當用戶卸載您的應用程序時,只有將應用程序的文件保存在經過使用getExternalFilesDir()獲取的目錄時,系統纔會今後處刪除應用程序的文件。
  • 對於不須要訪問限制的文件以及要與其餘應用程序共享或容許用戶使用計算機訪問的文件,外部存儲是最佳位置。

將文件保存在內部存儲上

您的應用程序的內部存儲目錄由您的應用程序包名稱指定在Android文件系統的特殊位置,可使用如下API訪問。緩存

注意:與外部存儲目錄不一樣,您的應用程序不須要任何系統權限來讀取和寫入這些方法返回的內部目錄。bash

寫一個文件

將文件保存到內部存儲時,能夠經過調用如下兩種方法獲取相應的目錄,進一步操做Filemarkdown

  • getFilesDir():返回File表示應用程序的內部目錄。
  • getCacheDir():返回File表示應用程序臨時緩存文件的內部目錄。

要在其中一個目錄中建立新文件,可使用File()構造函數,傳遞給File上述方法提供的指定內部存儲目錄的方法。app

例如:ide

File file = new File(context.getFilesDir(), filename);
複製代碼

或者,您能夠調用openFileOutput()以獲取FileOutputStream 寫入內部目錄中的文件的內容。函數

例如,如下是如何將一些文本寫入文件:url

String filename = "myfile";
String fileContents = "Hello world!";
FileOutputStream outputStream;

try {
    outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
    outputStream.write(fileContents.getBytes());
    outputStream.close();
} catch (Exception e) {
    e.printStackTrace();
}
複製代碼

MODE_PRIVATE 將會建立文件(或替換具備相同名稱的文件),並將其設爲應用的私有文件。 其餘可用模式包括:MODE_APPENDMODE_WORLD_READABLEMODE_WORLD_WRITEABLEspa

請注意,該openFileOutput()方法須要文件模式參數。傳遞 MODE_PRIVATE 將會建立文件(或替換具備相同名稱的文件),並將其設爲應用的私有文件。 其餘可用模式包括:MODE_APPENDMODE_WORLD_READABLEMODE_WORLD_WRITEABLE

自 API 級別 17 以來,常量 MODE_WORLD_READABLEMODE_WORLD_WRITEABLE 已被棄用。從 Android N(7.0,API24) 開始,使用這些常量將會致使引起 SecurityException。這意味着,面向 Android N 和更高版本的應用沒法按名稱共享私有文件,嘗試共享「file://」URI 將會致使引起 FileUriExposedException。 若是您的應用須要與其餘應用共享私有文件,則能夠將 FileProviderFLAG_GRANT_READ_URI_PERMISSION配合使用

在Android 6.0(API級別23)及更低級別上,若是您將文件模式設置爲全局可讀,則其餘應用程序能夠讀取您的內部文件。可是,其餘應用必須知道您的應用包名稱和文件名。除非您明確將文件設置爲可讀或可寫,不然其餘應用程序沒法瀏覽您的內部目錄而且沒有讀取或寫入權限·所以,只要您在內部存儲上使用MODE_PRIVATE標記您的文件,其餘應用就永遠沒法訪問它們

寫一個緩存文件

若是你須要緩存一些文件,你應該使用 createTempFile()

例如,如下方法從URL中提取文件名,並在應用程序的內部緩存目錄中建立具備該名稱的文件:

private File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(fileName, null, context.getCacheDir());
    } catch (IOException e) {
        // Error while creating file
    }
    return file;
}
複製代碼

使用createTempFile()建立的文件放置在應用程序專用的緩存目錄中。您應該按期刪除再也不須要的文件。

注意: 若是系統存儲空間不足,可能會在沒有警告的狀況下刪除緩存文件,所以請確保在讀取以前檢查緩存文件是否存在。

打開現有文件

要讀取現有文件,請調用openFileInput(name),傳遞文件名。

您能夠經過調用獲取全部應用程序文件名的數組 fileList()

注意:若是您須要在應用程序中打包一個可在安裝時訪問的文件,請將該文件保存在項目的res/raw/目錄中。您可使用openRawResource()傳遞資源ID 打開這些文件。此方法返回可用於讀取文件的方法。您沒法寫入原始文件。

打開一個目錄

您可使用如下方法在內部文件系統上打開目錄:

  • getFilesDir():返回File表示文件系統上與您的應用惟一關聯的目錄。
  • getDir(name, mode):在應用程序的惟一文件系統目錄中建立新目錄(或打開現有目錄)。此新目錄顯示在提供的目錄中getFilesDir()
  • getCacheDir():返回File表示文件系統上與您的應用惟一關聯的緩存目錄。此目錄適用於臨時文件,應按期清理。若是磁盤空間不足,系統可能會刪除那裏的文件,所以請確保在讀取以前檢查緩存文件是否存在。

要在其中一個目錄中建立新文件,可使用 File()構造函數,傳遞File上述方法之一提供的指定內部存儲目錄的對象。

例如:

File directory = context.getFilesDir();
File file = new File(directory, filename);
複製代碼

將文件保存在外部存儲上

使用外部存儲很是適合您要與其餘應用共享容許用戶使用計算機訪問的文件

請求存儲權限驗證存儲可用後,您能夠保存兩種不一樣類型的文件:

  • 公共文件:應該可供其餘應用程序和用戶無償使用的文件。當用戶卸載您的應用時,這些文件應該仍然可供用戶使用。例如,應用程序或其餘下載文件捕獲的照片應保存爲公共文件。
  • 私人文件:合法屬於您的應用的文件,將在用戶卸載您的應用時刪除。雖然這些文件在技術上可由用戶和其餘應用程序訪問,由於它們位於外部存儲上,但它們不會爲應用程序外的用戶提供價值。

注意若是用戶卸下SD卡或將設備鏈接到計算機,外部存儲可能會變得不可用。而且用戶和具備READ_EXTERNAL_STORAGE 權限的其餘應用程序仍然能夠看到這些文件。所以,若是您的應用程序的功能取決於這些文件,或者您須要徹底限制訪問,則應將文件寫入內部存儲。

請求外部存儲權限

要寫入公共外部存儲,您必須在清單文件中請求WRITE_EXTERNAL_STORAGE權限:

<uses-permission android:name = 「android.permission.WRITE_EXTERNAL_STORAGE」 />  
複製代碼

若是您的應用使用該WRITE_EXTERNAL_STORAGE權限,則它也隱式擁有讀取外部存儲的權限。

若是您的應用只須要讀取外部存儲(但不能寫入),那麼您須要聲明 READ_EXTERNAL_STORAGE權限:

<uses-permission android:name = 「android.permission.READ_EXTERNAL_STORAGE」 />
複製代碼

從Android 4.4(API級別19)開始,在應用程序的私有外部存儲目錄中讀取或寫入文件 - 使用getExternalFilesDir() 訪問, 不須要READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 權限。所以,若是您的應用支持Android 4.3(API級別18)及更低版本,而且您只想訪問專用外部存儲目錄,則應經過添加maxSdkVersion 屬性聲明僅在較低版本的Android上請求權限 :

<uses-permission android:name = 「android.permission.WRITE_EXTERNAL_STORAGE」
android:maxSdkVersion = 「18」 /> 
複製代碼

驗證外部存儲是否可用

因爲外部存儲可能不可用 - 例如當用戶將存儲裝置安裝到PC或已移除提供外部存儲的SD卡時 - 您應始終在訪問以前驗證該卷是否可用。您能夠經過調用來查詢外部存儲狀態getExternalStorageState()。若是返回狀態爲MEDIA_MOUNTED,則能夠讀取和寫入文件。若是是MEDIA_MOUNTED_READ_ONLY,則只能讀取文件。

例如,如下方法可用於肯定存儲可用性:

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}
複製代碼

保存到外部公共目錄

若是要將公共文件保存在外部存儲上,請使用該 getExternalStoragePublicDirectory()方法獲取File表示外部存儲上的相應目錄。該方法接受一個參數,指定要保存的文件類型,以即可以使用其餘公共文件(如DIRECTORY_MUSIC或) 對其進行邏輯組織DIRECTORY_PICTURES。例如:

public File getPublicAlbumStorageDir(String albumName) {
    // Get the directory for the user's public pictures directory. File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; } 複製代碼

若是要從Media Scanner中隱藏文件,請在外部文件目錄中包含一個名爲.nomedia空文件(請注意文件名 中的點前綴)。這能夠防止媒體掃描程序讀取您的媒體文件,並經過MediaStore內容提供商將其提供給其餘應用程序。

保存到外部私有目錄

若是要將文件保存在應用程序專用且外部提供程序沒法訪問的外部存儲上MediaStore,您能夠經過調用getExternalFilesDir()並向其傳遞一個名稱來獲取一個目錄,該目錄僅由您的應用程序使用, 該名稱指示您但願的目錄類型。以這種方式建立的每一個目錄都會添加到父目錄中,該目錄封裝了應用程序的全部外部存儲文件,系統會在用戶卸載應用程序時將其刪除。

public File getPrivateAlbumStorageDir(Context context, String albumName) {
    // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; } 複製代碼

若是沒有預約義的子目錄名稱適合您的文件,則能夠調用 getExternalFilesDir()並傳遞 null。這將返回外部存儲上應用程序私有目錄的根目錄。

請記住,getExternalFilesDir() 建立一個在用戶卸載應用程序時刪除的目錄。若是您保存的文件在用戶卸載應用程序後仍然可用 - 例如當您的應用程序捕獲照片而且用戶應保留這些照片時 - 您應該將文件保存到公共目錄。

在多個存儲位置之間選擇

有時,分配內部存儲器分區以用做外部存儲器的設備也提供SD卡插槽。這意味着該設備有兩個不一樣的外部存儲目錄,所以您須要選擇在將「私有」文件寫入外部存儲時使用哪一個目錄。

從Android 4.4(API級別19)開始,您能夠經過調用訪問這兩個位置getExternalFilesDirs(),該位置 返回一個包含每一個存儲位置條目的File數組。數組中的第一個條目被視爲主要外部存儲,您應該使用該位置,除非它已滿或不可用。

若是您的應用支持Android 4.3及更低版本,則應使用支持庫的靜態方法ContextCompat.getExternalFilesDirs()。這老是返回一個File數組,但若是設備運行的是Android 4.3及更低版本,那麼它只包含一個主外部存儲條目(若是有第二個存儲位置,則沒法在Android 4.3及更低版本上訪問它)。

刪除文件

您應該始終刪除您的應用再也不須要的文件。刪除文件最直接的方法是調用File對象delete()方法。

myFile.delete();
複製代碼

若是文件保存在內部存儲器上,您還能夠經過調用ContextdeleteFile()來查找和刪除文件:

myContext.deleteFile(fileName );
複製代碼

注意:當用戶卸載您的應用時,Android系統會刪除如下內容:

  • 您在內部存儲上保存的全部文件。
  • 使用getExternalFilesDir()保存在外部存儲的全部文件
  • 可是,您應該手動刪除getCacheDir()按期建立的全部緩存文件, 並按期刪除再也不須要的其餘文件。

Android 目錄總結

($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()
        |
        +- ???
複製代碼
相關文章
相關標籤/搜索