Android官方开发文档Training系列课程中文版:数据存储之文件存储
原文地址:http://android.xsoftlab.net/training/basics/data-storage/files.html
Android使用的文件系統和其它平臺的磁碟式文件系統很相似。這節課描述了如何通過FileAPI在Android文件系統上進行讀取文件和寫入文件的操作。
一個File對象適合被用來按照從頭到尾的方式讀取或寫入大量的數據,它不適合被用來跳躍式訪問,也就是隨機訪問。舉個例子,這對圖片文件或者任何基于網絡的數據交換是非常適合的。
這節課展示了如何執行APP中最基本的文件相關任務。這節課假定你有Linux的文件系統基礎以及Java標準的輸入輸出基礎。
選擇內部或外部存儲器
所有的Android設備有兩個文件存儲分區:”internal”和”external”分區。這些名詞來自于較早的Android版本,當大多數的設備提供了內置的固定內存,會添加一個可移除的存儲器媒體比如micro SD卡(外部存儲器)。一些設備固定的存儲器空間劃分為”internal”和”external”分區,所以甚至是沒有可移除的存儲媒體,那么仍然還是會有兩個存儲器空間。API會表現出相同的行為,無論外部存儲器是不是可移除的。下面是對每個存儲器空間的事實總結。
Internal storage:
- 它總是可用的。
- 默認情況下APP將文件保存在這里,只允許該APP可以訪問。
- 如果用戶卸載了你的APP,那么系統會將APP的所有文件從這里移除。
如果你不想讓用戶或者其它APP訪問你文件的話,內部存儲器是一個合適的地方。
External storage:
- 它并不總是可用的,因為用戶可能將外部存儲掛載為USB存儲器,和另外一些可能會被從設備上移除的情況。
- 它是全局可訪問的,所以如果文件存儲在這里,它可能會你的控制范圍之外被訪問。
- 當用戶卸載了你的APP,系統只會刪除你在getExternalFilesDir()返回目錄中保存的文件。
如果不需要訪問限制的話,外部存儲器是一個合適的地方,它可以另其它的APP訪問你的文件,用戶也可以通過電腦訪問這些文件。
Note:盡管APP安裝的時候會默認被安裝在內部存儲器內,不過你可以在清單文件中通過指定android:installLocation屬性來指定應用安裝的位置。如果APK的尺寸很大,并且外部存儲空間比內部存儲空間大的話,用戶會很欣賞這點。更多相關信息,請參見: App Install Location。
獲取外部存儲器的權限
如果要在外部存儲器寫入文件,你必須在清單文件中請求WRITE_EXTERNAL_STORAGE權限。
<manifest ...><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />... </manifest>警告:目前所有的APP都有能力訪問外部存儲器而不需要指定權限。然而,這會在未來的發行版做更改。如果你的APP需要讀取外部存儲器的數據(不是寫入),那么你需要聲明READ_EXTERNAL_STORAGE權限,來確保你的APP還可以在未來的版本中繼續工作,在更改生效之前,你應該現在就聲明這項權限。
<manifest ...><uses-permission > android:name="android.permission.READ_EXTERNAL_STORAGE" />... </manifest>然而,如果你的APP使用了WRITE_EXTERNAL_STORAGE權限,那么它會隱性的含有讀取外部存儲器的權限。
你在內部存儲器中保存文件不要任何權限。你的應用程序總是有權讀取和寫入文件到內部存儲器目錄。
保存文件到內部存儲器
當要保存文件到內部存儲器時,你可以通過下面兩個方法中的一個獲得一個合適的目錄對象File:
getFilesDir() 返回一個代表APP的內部存儲器目錄的File對象。
getCacheDir() 返回一個File對象,這個對象代表了APP在內部存儲器中的臨時文件緩存目錄。為了確保文件不再需要的時候可以刪除到它,應該在任何給定時間內對內存的數量實現一個合理的尺寸限制,比如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; }**Note:**APP的內部存儲路徑是通過APP的包名特別指定的,并且被放置在Android文件系統中一個特殊的位置。技術上講,其它的APP是可以讀取你的內部文件的,如果你設置的文件模式是可讀的。然而,其它的APP也應該知道你APP的包名和文件名。其它的APP不能瀏覽你的內部目錄,并且沒有訪問和寫入的權限,除非你特別設置了文件是和讀寫的。所以,一旦你對內部存儲器文件使用了MODE_PRIVATE模式,那么其它APP永遠不可能訪問到這些文件。
保存文件到外部存儲器
因為外部存儲器有可能是不可用的,比如當用戶將它掛載到了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; }盡管外部存儲器是可以被用戶或者其它APP修改的,這里有兩種你可能會用到的文件保存類別。
Public文件:
在這種情況下,文件應該會被其它APP或者用戶隨便訪問。當用戶卸載了你的APP,那么這些文件還會保留給用戶。
舉個例子,APP捕獲的照片或者下載的其它文件。
Private文件:
這些文件會正當的屬于你的APP,并且在用戶卸載APP的時候會刪除它們。盡管這些文件從技術上講是可以被用戶或者其它APP訪問的,因為這些文件是在外部存儲器上。實際上這些文件不會提供給APP之外的用戶。當用戶卸載了APP,那么系統會將這些文件從外部存儲器中刪除。
舉個例子,APP下載的附加資源或者臨時的媒體文件。
如果你想在外部存儲器上保存公開的文件,使用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()方法獲取合適的目錄,然后給它傳你喜歡的一個類型的目錄名稱。每一個目錄會通過這種方式創建并被添加到父目錄中,以便封裝所有在外部存儲器上的文件,當用戶卸載APP的時候會刪除它們。
下面這個方法你可使用它創建一個個人的相冊目錄:
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,這會返回APP在外部存儲器上私有目錄的根目錄。
記住在getExternalFilesDir()目錄中創建的目錄會隨著APP的卸載被刪除。如果你想在刪除之后仍然保留這些文件,比如你的APP是個攝像機應用,用戶希望保留下來這些照片,你應該使用getExternalStoragePublicDirectory()方法。
無論你使用的是getExternalStoragePublicDirectory()要共享文件還是使用getExternalFilesDir()保護你APP內的文件,你使用API提供的常量比如DIRECTORY_PICTURES作為目錄的名稱來說是非常重要的。這些目錄的名稱可以為系統合適的對待,舉個例子,如果文件存儲在DIRECTORY_RINGTONES的目錄中,系統媒體掃描器會將它識別為鈴聲文件,而不是音樂文件。
查詢剩余空間
如果你可以提前知道你保存的數據有多少,你可以查詢是否有足夠的空間可以使用,通過調用getFreeSpace()或者getTotalSpace()達到這一點。這些方法分別提供了當前可用空間和磁盤容量上的總空間,這些信息有助于避免填充的磁盤容量超過固定的閾值。
然而,系統不會保證你可以寫入與getFreeSpace()返回的剩余空間容量相當的數據。如果返回的數字是幾MB,大于你想存儲的數據大小,又或者系統當前使用容量少于總容量的90%,那么現在處理可能是安全的。否則,你可能不適合寫入到存儲器中。
Note:如果你在保存文件之前不檢查剩余可用空間,你可以試著以正確的方式寫入文件,如果事件發生了,那么可能會引起IOException異常。如果你不知道你需要的精確空間,那么你可能就需要這么去做。舉個例子,如果你在存儲文件之前從PNG編碼改為了JPEG編碼,你可能就不會提前知道文件的大小。
刪除文件
你應該在文件不再需要的時候將它們刪除。最直接了當的方式就是擁有一個已經打開了的文件的引用,然后調用它的delete()方法。
myFile.delete();如果文件被存在了內部存儲器上,你也可以要求Context定位,然后調用deleteFile()方法刪除文件。
myContext.deleteFile(fileName);Note:當用戶卸載了你的APP,Android系統會刪除以下文件:
所有保存在內部存儲器中的文件
所有保存getExternalFilesDir()目錄下的外部存儲器中的文件
無論如何,你應該定期手動刪除所有的緩存文件,即便是通過getCacheDir()方法創建的,還應該定期刪除你不再需要的其它文件。
總結
以上是生活随笔為你收集整理的Android官方开发文档Training系列课程中文版:数据存储之文件存储的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 热点事件发现、演化及时间线Timelin
- 下一篇: 由Actvity启动模式之SingleI