保存數據 之 保存文件

android所使用的文件系統類似與其他系統的那種基礎存儲盤的文件系統。這一節主要說一下android文件系統如何利用 File 這個API來讀寫文件。

File 對象適用於按照從頭到尾的順序讀寫大的數據,中間不會有部分內容的跳過操作。比方說,這個就很適用於圖片文件或者是通過網絡傳輸的文件的保存和讀取。

這一節裏面說一下在你的APP裏面如何進行和文件相關的基本操作。這個課程假設你對linux文件系統和java.io裏面的輸入\輸入API有一定的熟悉.

谷歌官方:點擊打開鏈接

選擇外部或者內部存儲器

android設備有2個存儲區域:“internal”(外部)和“external”(內部)存儲器。這個命名來自於早期的android,當時android提供一個內建的固態存儲器(internal),外面可以加一個多媒體存儲器,例如micro SD卡(external).有的設備會把它的固態存儲器,分爲2個部分,一個當做內部存儲器,一個部分當做外部存儲器,所以,即使外面沒有一個可移除的SD卡,這個設備也還是有2個存儲區域,當我們用系統的相關函數去獲取外部和外部的存儲路徑的時候,不管外部存儲器移走還是沒有,結果都是一樣的。

下面是對每個存儲區域的一些實際總結。

內部存儲器:

總是可用的

保存在這裏的文件只對這個文件的所有者有權限訪問操作

當用戶卸載掉APP的時候,這個APP存儲在內部存儲器的文件將會全部被刪除

內部存儲器是你存放一些不希望別的用戶或者APP訪問的文件的最好位置

 

外部存儲器:

不是總是可用的,因爲用戶有時候會把它當做一個USB存儲器或者是移除掉它

是通用可讀的,所以放在這裏的文件,可用被其他的APP讀取,無法控制

當用戶卸載APP的時候,僅僅是會自動刪除存儲在getExternalFilesDir()方法返回的路徑下的文件

外部存儲器適合存放那些不敏感的,不需要限制的以及那些希望別的APP也能訪問到的數據

提示:儘管APP默認是安裝在內部存儲器,但是你也可以利用android:installLocation 在manifest裏面指定你的APP安裝到外部存儲器。當用戶在有一個比內部存儲器大很多的外部存儲器的時候,如果安裝一個很大的APK包,那麼就會很在意這一點。想了解更多,請閱讀:App Install Location.

獲取外部存儲器的權限

如果想往外部存儲器寫數據,APP必須要在manifest文件裏面聲明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這個寫權限,也就意味着有了讀權限,雖然沒有明確的指明。

保存文件到內部存儲器上不需要任何的權限。APP一直都用在內部存儲器上讀寫的權限。

保存文件到內部存儲器

如果要保存文件到內部存儲器,首先要獲取一個合適的路徑,可以調用下面2個函數中的一個來獲取,返回的是一個包含有路徑的File對象

getFilesDir() 返回File對象代表着你的APP可以使用的內部存儲路徑

getCacheDir() 返回的File對象代表着你的APP的可以使用的臨時緩存文件的路徑。在不需要的這個文件的時候,要保證刪除掉它,並且在創建的時候,限制一個合適的大小,比方說1M。如果系統運行時,存儲空間很小的時候,它可能會直接刪除緩存問題,並且不會給出任何警告。

當需要在上面2者的任意一個地方創建文件的時候,可以利用File的構造函數,把上面2個函數獲取的路徑傳遞給構造函數,再加上文件名,就可以了:

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裏面提取名字作爲文件名,然後用這個名字在APP可用的內部緩存路徑下面創建一個緩存文件。

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;
}

注意:APP使用的內部存儲器的路徑,是依據APP的包名有android文件系統指定的。從技術角度講,如果把APP的內部存儲的文件設置爲可讀,那麼其他的APP也可以讀取到這些文件的。但是前提是其他的APP要知道你的包名和裏面創建的文件的文件名。其他APP不可以瀏覽你的APP使用的內部存儲路徑,也不可以讀寫你APP的文件,除非你的APP明確指出APP創建的文件是可讀寫的。如果你在你創建的內部存儲器裏面的文件都使用了MODE_PRIVATE屬性,那麼他們就永遠也沒辦法被其他的APP獲取到。

保存文件到外部存儲器

因爲外部存儲器不是總是可用的,比方說被移除,或者鏈接到PC作爲usb存儲器使用,所以在使用之前要檢查它是否可用。可以調用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更改,在這裏面,依然可以存放這2中屬性的文件:

公有文件:文件對其他的APP或者用戶都是可用的。當你的應用卸載的時候,這些文件會繼續保留着。例如你的APP拍到的照片或者是其他的下載文件。

私有文件:你的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創建的文件在APP被卸載之後依然存在,那麼就使用getExternalStoragePublicDirectory().這個方法來獲取存儲路徑。不管是調用getExternalStoragePublicDirectory()來創建公有文件還是getExternalFilesDir()來創建私有文件,使用API提供的類型參數來指定你的文件的類型都很重要,例如DIRECTORY_PICTURES。這種方式可以保證你的文件會被系統自動處理歸類。例如,如果用DIRECTORY_RINGTONES來指定你保存的文件類型,系統在掃描你的文件時候,會把這個文件當做鈴聲而不是音樂文件。

查詢剩餘空間

有時候需要預先知道存儲空間剩餘的大小,防止存儲大文件出現IO異常,存儲空間不夠用的情況。可以調用getFreeSpace()或者getTotalSpace()來獲取的空間大小。前者提供當前可用的空間,後者獲取實際上的總的存儲空間。系統不保證你的可以寫的文件大小和getFreeSpace獲取到的一樣大,如果你獲取到的比你要寫入的文件大一些,或者存儲盤距離滿還有90%的空間,一般都沒什麼問題,其他情況下,最好不要往存儲器寫數據了。

注意:也不是說每次寫之前必須檢查可用空間,可用在寫的過程中捕獲IO異常。

刪除文件

不需要的時候要刪除創建的文件。最簡單的方法是用File打開一個文件,然後調用delete方法。

myFile.delete();

如果是存儲在內部存儲器上的,可以利用Context定位文件,調用deleteFile()刪除文件。

myContext.deleteFile(fileName);

同樣的記住要手動的刪除getCacheDir()下創建的不需要臨時文件。

發佈了35 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章