【Android Training - 04】保存數據 [ Lesson 2 - 保存文件]

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://developer.android.com/training/basics/data-storage/files.html,歡迎一起交流!

轉載請註明出自:http://blog.csdn.net/kesenhoo,謝謝!






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章