Android使用與其他平臺類似的基於磁盤文件系統(disk-based file systems)。這節課會描述如何在Android文件系統上使用 File
的讀寫APIs。File
對象非常適合用來讀寫那種流式順序的數據。例如,很適合用來讀寫圖片文件或者是網絡中交換的數據。這節課會演示在app中如何執行基本的文件操作任務。假定你已經對linux的文件系統與java.io中標準的I/O
APIs有一定認識。
Choose Internal or External Storage [選擇Internal還是External存儲]
所有的Android設備都有兩個文件存儲區域:"internal" 與 "external" 存儲。 那兩個名稱來自與早先的Android系統中,那個時候大多數的設備都內置了不可變的內存(internal storage),然後再加上一個類似SD card(external storage)這樣可以卸載的存儲部件。後來有一些設備把"internal" 與 "external" 的部分都做成不可卸載的內置存儲了,雖然如此,但是這一整塊還是從邏輯上有被劃分爲"internal"與"external"的。只是現在不再以是否可以卸載來區分了。 下面列出了兩者的區別:
Internal storage:
- 總是可用的
- 這裏的文件默認是隻能被你的app所訪問的。
- 當用戶卸載你的app的時候,系統會把internal裏面的相關文件都清除乾淨。
- Internal是在你想確保不被用戶與其他app所訪問的最佳存儲區域。
External storage:
- 並不總是可用的,因爲用戶可以選擇把這部分作爲USB存儲模式,這樣就不可以訪問了。
- 是大家都可以訪問的,因此保存到這裏的文件是失去訪問控制權限的。
- 當用戶卸載你的app時,系統僅僅會刪除external根目錄(getExternalFilesDir())下的相關文件。
- External是在你不需要嚴格的訪問權限並且你希望這些文件能夠被其他app所共享或者是允許用戶通過電腦訪問時的最佳存儲區域。
Tip: 儘管app是默認被安裝到internal storage的,你還是可以通過在程序的manifest文件中聲明android:installLocation
屬性來指定程序也可以被安裝到external
storage。當某個程序的安裝文件很大,用戶會傾向這個程序能夠提供安裝到external storage的選項。更多安裝信息,請參考 App Install Location。
Obtain Permissions for External Storage [獲取External存儲的權限]
爲了寫數據到external storage, 你必須在你的manifest文件中請求 WRITE_EXTERNAL_STORAGE
權限:
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>Caution: 目前,所有的apps都可以在不指定寫的權限下做讀external storage的動作。但是,這會在以後的版本中被修正。如果你的app需要讀的權限(不是寫), 那麼你需要聲明
READ_EXTERNAL_STORAGE
權限。爲了確保你的app能夠在正常工作,你需要現在就聲明讀權限。但是,如果你的程序有聲明寫的權限,那麼就默認有了讀的權限。
<manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ... </manifest>對於internal storage,你不需要聲明任何權限,因爲你的程序默認就有讀寫程序目錄下的文件的權限。
Save a File on Internal Storage [保存文件到Internal Storage]
當保存文件到internal storage時,你可以通過執行下面兩個方法之一來獲取合適的目錄作爲File的對象:
getFilesDir()
返回一個 File
,代表了你的app的internal目錄。
getCacheDir()
返回一個 File
,代表了你的app的internal緩存目錄。請確保這個目錄下的文件在一旦不再需要的時候能夠馬上被刪除,還有請給予一個合理的大小,例如1MB
。如果系統的內存不夠,會自行選擇刪除緩存文件。
爲了在那些目錄下創建一個新的文件,你可以使用 File()
構造器,如下:
File file = new File(context.getFilesDir(), filename);同樣,你也可以執行
openFileOutput()
來獲取一個 FileOutputStream
用來寫文件到internal目錄。如下:
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的internal storage 目錄是以你的app的包名作爲標識存放在Android文件系統的特定目錄下[data/data/com.example.xx]。 從技術上講,如果你設置文件爲可讀的,那麼其他app就可以讀取你的internal文件。然而,其他app需要知道你的包名與文件名。若是你沒有設置爲可讀或者可寫,其他app是沒有辦法讀寫的。因此只要你使用
MODE_PRIVATE
,那麼這些文件就不可能被其他app所訪問。Save a File on External Storage [保存文件到External Storage]
因爲external storage可能是不可用的,那麼你應該在訪問之前去檢查是否可用。你可以通過執行getExternalStorageState()
來查詢external
storage的狀態。如果返回的狀態是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; }
儘管external storage對與用戶與其他app是可修改的,那麼你可能會保存下面兩種類型的文件。
- Public files
- 這些文件對與用戶與其他app來說是public的,當用戶卸載你的app時,這些文件應該保留。
例如,那些被你的app拍攝的圖片或者下載的文件。
- Private files
- 這些文件應該是被你的app所擁有的,它們應該在你的app被卸載時刪除掉。儘管那些文件從技術上可以被用戶與其他app所訪問,實際上那些文件對於其他app是沒有意義的。所以,當用戶卸載你的app時,系統會刪除你的app的private目錄。
例如,那些被你的app下載的緩存文件。
如果你想要保存文件爲public形式的,請使用 getExternalStoragePublicDirectory()
方法來獲取一個 File
對象來表示存儲在external
storage的目錄。這個方法會需要你帶有一個特定的參數來指定這些public的文件類型,以便於與其他public文件進行分類。參數類型包括 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()
來獲取相應的目錄,並且傳遞一個指示文件類型的參數。
每一個以這種方式創建的目錄都會被添加到external storage封裝你的app目錄下的參數文件夾下(如下則是albumName)。這下面的文件會在用戶卸載你的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在external storage下的private的根目錄。
請記住, getExternalFilesDir()
方法會創建的目錄會在app被卸載時被系統刪除。如果你的文件想在app被刪除時仍然保留,請使用getExternalStoragePublicDirectory()
.
不管你是使用 getExternalStoragePublicDirectory()
來存儲可以共享的文件,還是使用 getExternalFilesDir()
來儲存那些對與你的app來說是私有的文件,有一點很重要,那就是你要使用那些類似DIRECTORY_PICTURES 的
API的常量。那些目錄類型參數可以確保那些文件被系統正確的對待。例如,那些以DIRECTORY_RINGTONES
類型保存的文件就會被系統的media
scanner認爲是ringtone而不是音樂。
Query Free Space [查詢剩餘空間]
如果你事先知道你想要保存的文件大小,你可以通過執行 getFreeSpace()
or getTotalSpace()
來判斷是否有足夠的空間來保存文件,從而避免發生IOException。
那些方法提供了當前可用的空間還有存儲系統的總容量。
然而,系統並不會授權你寫入通過getFreeSpace()
.查詢到的容量文件,
如果查詢的剩餘容易比你的文件大小多幾MB,或者說文件系統使用率還不足90%,這樣則可以繼續進行寫的操作,否則你最好不要寫進去。
Note: 你並沒有強制要求在寫文件之前一定有要去檢查剩餘容量。你可以嘗試先做寫的動作,然後通過捕獲 IOException
。這種做法僅適合於你並不知道你想要寫的文件的確切大小。
Delete a File [刪除文件]
你應該在不需要使用某些文件的時候,刪除它。刪除文件最直接的方法是直接執行文件的 delete()
方法。
myFile.delete();
如果文件是保存在internal storage,你可以通過 Context
來訪問並通過執行deleteFile()
進行刪除
myContext.deleteFile(fileName);
Note: 當用戶卸載你的app時,android系統會刪除下面的文件:
- 所有保存到internal storage的文件。
- 所有使用
getExternalFilesDir()
方式保存在external storage的文件
然而,你應該手動刪除所有通過 getCacheDir()
方式創建的緩存文件,還有那些通常來說不會再用的文件。
轉載請註明出自:http://blog.csdn.net/kesenhoo,謝謝!