來源: html
http://developer.android.com/training/basics/data-storage/files.html java
Android使用了一種相似於其它平臺上基於磁盤文件系統的文件系統. 本節課描述瞭如何使用 File API在Android文件系統中讀寫文件. android
File 對象適用於用一種沒有跳躍的從開始一直到結尾的方式讀寫大量數據. 例如,它很適合經過網絡進行圖片文件或者任何其它的文件交換. 緩存
本課程展現瞭如何在 你的應用中進行文件相關的基礎操做. 本課程假定你熟悉Linux文件系統,還有java.io中的標準文件輸入/輸出操做. 安全
全部安卓設備都有兩個存儲區域: "內部" 和 "外部" 存儲. 這些名稱來自早期的安卓, 那時候大多數設備都提供內建的非易丟失內存 (內部存儲), 再加上一個可移除的存儲介質,好比微型SD卡 (外部存儲). 一些設備將永久存儲空間分紅「內部」和「外部」分區, 所以即便沒有可移除的存儲介質,也總會兩個存儲空間,而無論外部存儲是否是可移除的,API行爲都是同樣的. 下面的列表總結的每個存儲空間的一些要點. 網絡
內部存儲: app
當你想要確保不論是你的用戶仍是其它應用都能訪問你的文件,內部存儲是最合適的. ide
外部存儲: ui
外部存儲時保存那些不須要訪問限制的文件的最好地方,還有那些你想要同其它應用共享或者容許用戶使用計算機來訪問的文件 . 編碼
提示: 儘管應用默認被安裝到內存存儲, 其實你還能夠在manifest中指定android:installLocation屬性,那樣你的應用就能夠被安裝在外部存儲上了. 當APK的尺寸很是大,而且用戶擁有一個比內存存儲大得多的外部存儲時,這一選擇會受到歡迎. 更多的信息,能夠看看 應用存儲位置 .
爲了寫入外部存儲,你必須在你的manifest文件處請求WRITE_EXTERNAL_STORAGE權限 :
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
注意: 當前,全部的應用都有能力不須要一個特定的權限就能夠讀取內部存儲. 不過,這將會在未來的版本中改變. 若是你的應用須要讀取內部存儲(但不去寫入它), 那麼你將會須要聲明 READ_EXTERNAL_STORAGE 權限. 爲了確保你的應用能如預期的運做, 在變化尚未起做用以前,你如今就應該聲明這一權限.
<manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ... </manifest>
不過,若是你的應用使用了 WRITE_EXTERNAL_STORAGE 權限,那麼它也就隱含了讀取內部存儲的權限了 .
在內部存儲上保存文件不須要任何權限. 你的應用程序老是有在其內部存儲目錄中讀寫文件的權限.
當要在一個內部存儲中保存一個文件時,你能夠經過調用下面兩個方法的其中之一,來獲取相應的目錄 文件 :
getFilesDir()
返回一個表示你應用的內部路徑的 File .
返回一個表示你應用的臨時緩存內部路徑的 File . 要確保一旦文件再也不須要時都刪除一次,而且在任何給定時間你使用的內存都有一個合理的大小限制, 好比 1MB. 若是系統開始低存儲消耗的運行 , 他可能在沒有提示就刪除了你的緩存文件.
爲了在這些目錄中的一個裏面建立一個新的文件,你可使用 File() 構造器,傳入由上述指定了你的內部存儲路徑的方法提供的 File . 例如:
File file = new File(context.getFilesDir(), filename);
此外,你還能夠調用 openFileOutput() 來得到一個 FileOutputStream , 它會在你的內部路徑中寫入一個文件 . 例如,這裏是如何將一些文本寫入一個文件 :
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; }
注意: 你的應用的內部存儲路徑將用你的應用的包名,在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; }
儘管外部存儲能夠被用戶和其它應用修改,仍是有兩類文件你能夠保存在這裏 :
若是你想要在外部存儲上保存公共的文件,使用 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 中的文件能夠被系統的媒體搜索器歸爲鈴聲一類,而不是音樂 .
若是你事先知道要保存多少數據,你就能夠經過調用 getFreeSpace() 或者 getTotalSpace() 發現是否有足夠空間保存這些數據,而不會致使一個 IOException . 這些方法分別提供了存儲卷中當前有多少可用空間以及總空間. 這種信息在避免填充的數據量超過必定的閾值時也一樣有用 .
不過,系統並不能確保你能夠寫入同 getFreeSpace() 所獲取到的剩餘空間大小同等量的數據. 若是返回的數量比你想要保存的數據多幾個MB,或者若是文件系統佔率低於90%,那麼每每還算安全。不然,你可能就不該該再往裏面寫入了.
注意: 你並不必定要在保存你的文件以前檢查剩餘空間的數量. 你能夠嘗試首先寫入文件,而後獲取一個 IOException ,若是這個異常發生了的話 . 若是你並不知道你須要多少空間的時候,可能就得這麼作 . 例如,若是你在保存文件以前改變了文件的編碼方式,將一張PNG圖片轉換成了JPG的,你是不會事先知道文件的大小的 .
你應該老是刪除你再也不須要的文件。刪除一個文件最直接的方式是讓打開的文件引用自身調用 delete() .
myFile.delete();
若是文件被保存在外部存儲上,你也能夠經過調用deleteFile()叫 Context來定位並刪除一個文件 :
myContext.deleteFile(fileName);
注意: 當用戶卸載你的應用時,Android系統會刪除下面這些東西 :
不過,你應該按期手動的去刪除使用 getCacheDir() 建立的緩存文件,也要有規律的去刪除你再也不須要的文件.