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

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