注:本文翻譯自Google官方的Android Developers Training文檔,譯者技術一般,由于喜愛安卓而產生了翻譯的念頭,純屬個人興趣愛好。
原文鏈接: http://developer.android.com/training/basics/data-storage/files.html
Android使用的文件系統和其它平臺上使用的磁盤文件系統比較類似。這節課將描述如何通過 File 的APIs對Android文件系統進行讀寫文件。
一個文件對象適合于按既定的順序讀或者寫大量數據,
而非跳躍式地進行。例如,它適合于圖像文件或者任何在網絡上交換的數據。
這堂課將展示如何在你的應用中,執行與基本文件相關的任務。我們假設你熟悉Linux文件系統基礎知識,以及 java.io 中標準文件輸入/輸出流的APIs。
?
一). 選擇內存或外存
所有Android設備有兩個文件存儲區域:“ 內部 ( internal )”和“ 外部 ( external )”存儲。這兩個名字的由來要追溯到早期的Android,那時大多數設備提供內置的 非易失性存儲器(內存),加上一個可移除的閃存介質,比如迷你SD卡(外存)。一些設備將永久存儲空間劃分為“內部”和“外部”兩個部分,所以即使沒有閃存介質,仍然會有兩個存儲空間。與此同時,不管外存是否是可移除的,對于API來說沒有差異。下面將列舉出每個存儲空間的特性。
內存:
- 永遠可以獲取的到
- 默認情況下,存儲在這里的文件只有你的應用自身能獲取到
- 當用戶卸載了你的應用,系統會從內存中刪除所有該應用的相關文件
綜上所述,當你期望你的文件不會被用戶或者其他應用獲取時,內存將是最好的選擇。
外存:
- 它并不能永遠都可獲得,因為用戶可以將外存作為一個USB存儲而掛載起來,并且在一些情況下會把它從設備上移除
- 存儲在這里的文件可被任意訪問,而不在你的控制之內
- 當用戶刪除了你的應用時,只有在你將文件存儲在通過 getExternalFilesDir() 方法所得到的目錄下時,系統才會刪除你的文件
綜上所述,外存適合于存儲那些對訪問沒有限制的文件,以及你希望和其他應用共享的文件,或者你希望用戶可以通過電腦來獲取到的文件。
Tip:
雖然默認情況下應用匯存儲在內存,但你可以定義清單文件中的“ android:installLocation ”這一屬性字段,這樣你的應用就可以存儲在外存上。當APK文件大小很大,同時用戶擁有一個比內存空間要大的外存時,用戶會期望能夠這么做。更多信息可以閱讀: App Install Location 。
?
二). 獲得操控外存的權限許可
為了獲得寫入外存的權限,你必須在你的清單文件( manifest file )中聲明“ WRITE_EXTERNAL_STORAGE ”的授權許可:
< manifest ... > < uses-permission android:name ="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </ manifest >
Caution:
目前,所有的應用都可以在沒有特殊權限許可的情況下讀取外存。然而這將會在未來的某個版本下改變。如果你的應用需要讀取外存(而不需要寫),那么你需要聲明“ READ_EXTERNAL_STORAGE ”的權限許可,以此保證你的應用在未來版本更新以后還可以正常工作。務必在改變生效之前,現在就聲明這個權限許可。
< manifest ... > < uses-permission android:name ="android.permission.READ_EXTERNAL_STORAGE" /> ... </ manifest >然而,如果你的應用使用 “ WRITE_EXTERNAL_STORAGE ”的授權許可,那么它暗示了同時還擁有讀外存的權限。
在內存中保存文件不需要任何權限許可。你的應用永遠都具有讀和寫在內存中其自身所對應的目錄的權限。
?
三). 在內存中保存一個文件
當將一個文件存入內存時,你可以通過調用以下任一一種方法來取得合適的目錄作為一個 File 對象:
- getFilesDir() :返回一個 File 對象,它 代表了你的應用所擁有的內存中的一個目錄。
- getCacheDir() :返回一個 File 對象,它代表了存放應用的臨時緩存文件的內存目錄。請確保刪除每一個不再需要的文件,同時制定一個任何時刻你能使用的存儲空間的大小限制,比如1MB。如果系統在運行時存儲空間不足,它可能會在沒有任何警告的情況下刪除你的緩存文件。
為了在上述任何一個目錄中創建文件,你可以使用構造函數 File() ,傳遞給它上述兩個方法中的一個來特定你的內存中的目錄路徑。例如:
File file =
new
File(context.getFilesDir(), filename);
另外,你可以調用 openFileOutput() 來獲得一個文件輸出流,以此將數據寫入你的內存目錄中的一個文件。例如:
String filename = "myfile" ; String string = "Hello world!" ; FileOutputStream outputStream; try { outputStream = openFileOutput(filename, Context.MODE_PRIVATE); outputStream.write(string.getBytes()); outputStream.close(); } catch (Exception e) { e.printStackTrace(); }
或者,如果你需要緩存一個文件,你應該使用 createTempFile() 方法。例如,下面的方法從一個 URL 中提取出文件名,然后利用該名字創建一個文件,在你的應用的內存目錄中:
public 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; }
Note:
你的應用的內存目錄的位置是在Android文件系統中的一個特殊的位置,它由你的應用的包名所指定。從技術上來講,如果你將文件的模式設置為可讀,那么另一個應用是可以讀取你的內部文件的。然而,這是在其他應用指導你的應用的包名以及相應的文件名的情況下。其他應用瀏覽你的內部目錄并且沒有讀或寫的權力,除非你顯示地將文件設置為了可讀或可寫。所以只要你為你存放在內存中文件使用了 MODE_PRIVATE 標識,它們將永遠無法被其他應用所訪問到。
?
四). 在外存上保存一個文件
因為外存可能是無法獲得的,比如當用戶已經把存儲掛載至PC上,或者已經移除了提供外存的SD卡。你應該在每次訪問它之前先確認對應的卷是否存在。你可以通過調用
getExternalStorageState()
來查詢外存的狀態。如果返回的狀態是
MEDIA_MOUNTED
,那么你可以讀和寫你的文件。例如,下面的方法用來確認存儲是否可用:
/* 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 ; }
雖然外存可被用戶或其他應用修改,但是你在這里存儲的文件可以分為兩類:
公有文件(Public files):
這些文件可以被用戶和其他應用任意訪問。當用戶卸載了你的應用,這些文件將仍然保留。例如,你的應用所拍攝的圖片或其他下載的文件。
私有文件(Private files):
屬于你的應用的合法文件,用戶卸載你的應用時,這些文件也會被同時刪除。雖然從技術上說,因為這些文件存儲于外存,所以它們可以被用戶或其他應用訪問到,但實際上這些文件在你的應用范圍之外向用戶提供任何數據。當用戶卸載了你的應用時,系統會刪除所有你的應用外部私有目錄下的文件。此類文件的例子有:你應用所下載的額外的資源文件或者臨時的多媒體文件。
如果你希望在外存中存儲公有文件,使用
getExternalStoragePublicDirectory()
來獲得一個
File
對象,它代表了外存上一個合適的目錄。這個方法接受一個參數,該參數指定了你希望存儲的文件類型,這樣它們就能與其他公有文件一起被統一地管理了,文件類型諸如:
DIRECTORY_MUSIC
或者
DIRECTORY_PICTURES
。
public File getAlbumStorageDir(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; }
如果你希望存儲屬于你應用的私有文件,那么你可以調用 getExternalFilesDir() 來獲得一個合適的目錄,同時傳遞給它一個名字來說明目錄類型(如果你喜歡的話)。每個通過這種方式創建的目錄會添加到一個父目錄下,以此把你的應用的所有外存文件都封裝起來。當用戶卸載應用時,他們會被刪除。
例如,下面的方法可以用來為一個個人相冊創建一個目錄:
public File getAlbumStorageDir(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() 所創建的目錄是在一個當用戶卸載你的應用時,會被一起刪除的目錄下的。如果你所保存的文件在用戶卸載應用后仍然需要存在(比如你的應用是一個相機軟件,用戶希望保留這些相片),那么你應該使用 getExternalStoragePublicDirectory() 。
不管你使用的是
getExternalStoragePublicDirectory()
(用于共享的文件),還是
getExternalFilesDir()
(用于私有的文件),使用API常量提供的目錄名(諸如
DIRECTORY_PICTURES
)是很重要的。這些目錄名保證了系統會正確地處理這些文件。例如,存儲于
DIRECTORY_RINGTONES
下的文件會被系統的多媒體掃描器分類為鈴聲而不是音樂。
?
五). 查詢空余空間
如果你能提前知道你要存儲多大的數據,那么你將知道是否有足夠的空間來存儲這些數據,從而避免
IO異常(
IOException
)。通過
調用
getFreeSpace()
或者
getTotalSpace()
這兩個方法可以實現上述的預期。這兩個方法分別提供了在存儲卷內的當前可用空間和總空間。這些信息還可以用來避免向存儲卷內填充超出閾值數量的數據。
然而,系統不會保證你可以寫入和
getFreeSpace()
所返回的可用空間一樣大小的數據。如果返回的數量比你希望存儲的數量多了幾兆,或者文件系統的使用率小于90%,那么繼續執行是沒有問題的。否則你可能無法寫入數據。
Note:
你不必在你存儲文件之前檢查可用空間的大小。你可以嘗試直接寫入文件,然后當異常發生時捕捉 IOException 。你需要這么做如果你不知道你具體需要多少空間。例如,如果你在保存之前轉變了文件的編碼(把PNG格式的圖片轉換為JPEG,此時你無法預知文件的大小)。
?
六). 刪除一個文件
你應該將不再需要的文件刪除。刪除文件最直接的方法是對文件對象自身調用 delete() 。
myFile.delete();
如果文件存儲于內存,你也可以通過 Context 來定位,然后調用 deleteFile() 刪除文件:
myContext.deleteFile(fileName);
Note:
當用戶卸載了你的應用,Android系統會刪除如下文件:
- 所有你在內存存儲的文件
- 所有你通過 getExternalFilesDir() 方法在外存存儲的文件
然而,你要定期手動地刪除所有通過 getCacheDir() 創建的臨時文件以及其它你不再需要的文件。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
