Android文件存儲(chǔ)之使用File

保存文件

Android 使用與其他平臺(tái)上基于磁盤的文件系統(tǒng)類似的文件系統(tǒng)怨喘。 本課程講述如何使用 Android 文件系統(tǒng)通過 File API 讀取和寫入文件。

File 對(duì)象適合按照從開始到結(jié)束的順序不跳過地讀取或?qū)懭氪罅繑?shù)據(jù)追他。 例如,它適合于圖片文件或通過網(wǎng)絡(luò)交換的任何內(nèi)容表蝙。

本課程展示如何在您的應(yīng)用中執(zhí)行基本的文件相關(guān)任務(wù)宠哄。本課程假定您熟悉 Linux 文件系統(tǒng)的基礎(chǔ)知識(shí)和 java.io 中的標(biāo)準(zhǔn)文件輸入/輸出 API。

選擇內(nèi)部或外部存儲(chǔ)

所有 Android 設(shè)備都有兩個(gè)文件存儲(chǔ)區(qū)域:“內(nèi)部”和“外部”存儲(chǔ)禽最。這些名稱在 Android 早期產(chǎn)生腺怯,當(dāng)時(shí)大多數(shù)設(shè)備都提供內(nèi)置的非易失性內(nèi)存(內(nèi)部存儲(chǔ)),以及移動(dòng)存儲(chǔ)介質(zhì)川无,比如微型 SD 卡(外部存儲(chǔ))呛占。一些設(shè)備將永久性存儲(chǔ)空間劃分為“內(nèi)部”和“外部”分區(qū),即便沒有移動(dòng)存儲(chǔ)介質(zhì)舀透,也始終有兩個(gè)存儲(chǔ)空間栓票,并且無論外部存儲(chǔ)設(shè)備是否可移動(dòng)决左,API 的行為均一致愕够。以下列表匯總了關(guān)于各個(gè)存儲(chǔ)空間的實(shí)際信息。

內(nèi)部存儲(chǔ):

它始終可用佛猛。
只有您的應(yīng)用可以訪問此處保存的文件惑芭。
當(dāng)用戶卸載您的應(yīng)用時(shí),系統(tǒng)會(huì)從內(nèi)部存儲(chǔ)中移除您的應(yīng)用的所有文件继找。
當(dāng)您希望確保用戶或其他應(yīng)用均無法訪問您的文件時(shí)遂跟,內(nèi)部存儲(chǔ)是最佳選擇。

外部存儲(chǔ):

它并非始終可用婴渡,因?yàn)橛脩艨刹捎?USB 存儲(chǔ)設(shè)備的形式裝載外部存儲(chǔ)幻锁,并在某些情況下會(huì)從設(shè)備中將其移除。
它是全局可讀的边臼,因此此處保存的文件可能不受您控制地被讀取哄尔。
當(dāng)用戶卸載您的應(yīng)用時(shí),只有在您通過 getExternalFilesDir() 將您的應(yīng)用的文件保存在目錄中時(shí)柠并,系統(tǒng)才會(huì)從此處移除您的應(yīng)用的文件岭接。
對(duì)于無需訪問限制以及您希望與其他應(yīng)用共享或允許用戶使用計(jì)算機(jī)訪問的文件,外部存儲(chǔ)是最佳位置臼予。

提示:盡管應(yīng)用默認(rèn)安裝在內(nèi)部存儲(chǔ)中鸣戴,但您可在您的清單文件中指定 android:installLocation 屬性,這樣您的應(yīng)用便可安裝在在外部存儲(chǔ)中粘拾。當(dāng) APK 非常大且它們的外部存儲(chǔ)空間大于內(nèi)部存儲(chǔ)時(shí)窄锅,用戶更青睞這個(gè)選擇。 如需了解詳細(xì)信息缰雇,請(qǐng)參閱應(yīng)用安裝位置酬滤。

添加讀寫權(quán)限在清單文件中
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

如果你只想讓用戶讀可以只獲得讀權(quán)限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
但是签餐,如果您的應(yīng)用使用 WRITE_EXTERNAL_STORAGE 權(quán)限,那么它也隱含讀取外部存儲(chǔ)的權(quán)限盯串。

將文件保存在內(nèi)部存儲(chǔ)中

在內(nèi)部存儲(chǔ)中保存文件時(shí)氯檐,您可以通過調(diào)用以下兩種方法之一獲取作為 File 的相應(yīng)目錄:

getFilesDir()
返回表示您的應(yīng)用的內(nèi)部目錄的 File 。
getCacheDir()
返回表示您的應(yīng)用臨時(shí)緩存文件的內(nèi)部目錄的 File体捏。 務(wù)必刪除所有不再需要的文件并對(duì)在指定時(shí)間您使用的內(nèi)存量實(shí)現(xiàn)合理大小限制冠摄,比如,1MB几缭。 如果在系統(tǒng)即將耗盡存儲(chǔ)河泳,它會(huì)在不進(jìn)行警告的情況下刪除您的緩存文件。

要在這些目錄之一中新建文件年栓,您可以使用 File() 構(gòu)造函數(shù)行您,傳遞指定您的內(nèi)部存儲(chǔ)目錄的上述方法之一所提供的 File。例如:

File file = new File(context.getFilesDir(), filename);

或者牧牢,您可以調(diào)用 openFileOutput() 獲取寫入到內(nèi)部目錄中的文件的 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();
}

或者否副,如果您需要緩存某些文件汉矿,您應(yīng)改用 createTempFile()。例如备禀,以下方法從 URL 提取文件名并正在您的應(yīng)用的內(nèi)部緩存目錄中以該名稱創(chuàng)建文件:

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

注:您的應(yīng)用的內(nèi)部存儲(chǔ)設(shè)備目錄由您的應(yīng)用在 Android 文件系統(tǒng)特定位置中的軟件包名稱指定洲拇。從技術(shù)上講,如果您將文件模式設(shè)置為可讀曲尸,那么赋续,另一應(yīng)用也可以讀取您的內(nèi)部文件。 但是另患,此應(yīng)用也需要知道您的應(yīng)用的軟件包名稱和文件名纽乱。 其他應(yīng)用無法瀏覽您的內(nèi)部目錄并且沒有讀寫權(quán)限,除非您明確將文件設(shè)置為可讀或可寫柴淘。 只要您為內(nèi)部存儲(chǔ)上的文件使用 MODE_PRIVATE迫淹,其他應(yīng)用便從不會(huì)訪問它們。

將文件保存在外部存儲(chǔ)中

由于外部存儲(chǔ)可能不可用—比如为严,當(dāng)用戶已將存儲(chǔ)裝載到電腦或已移除提供外部存儲(chǔ)的 SD 卡時(shí)—因此敛熬,在訪問它之前,您應(yīng)始終確認(rèn)其容量第股。 您可以通過調(diào)用 getExternalStorageState() 查詢外部存儲(chǔ)狀態(tài)应民。 如果返回的狀態(tài)為 MEDIA_MOUNTED,那么您可以對(duì)您的文件進(jìn)行讀寫。 例如诲锹,以下方法對(duì)于確定存儲(chǔ)可用性非常有用:

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

盡管外部存儲(chǔ)可被用戶和其他應(yīng)用進(jìn)行修改繁仁,但您可在此處保存兩類文件:

公共文件

應(yīng)供其他應(yīng)用和用戶自由使用的文件。 當(dāng)用戶卸載您的應(yīng)用時(shí)归园,用戶應(yīng)仍可以使用這些文件黄虱。
例如,您的應(yīng)用拍攝的照片或其他已下載的文件庸诱。

私有文件

屬于您的應(yīng)用且在用戶卸載您的應(yīng)用時(shí)應(yīng)予刪除的文件捻浦。 盡管這些文件在技術(shù)上可被用戶和其他應(yīng)用訪問(因?yàn)樗鼈兇鎯?chǔ)在外部存儲(chǔ)中), 但它們實(shí)際上不向您的應(yīng)用之外的用戶提供任何輸出值桥爽。 當(dāng)用戶卸載您的應(yīng)用時(shí)朱灿,系統(tǒng)會(huì)刪除應(yīng)用外部私有目錄中的所有文件。
例如钠四,您的應(yīng)用下載的其他資源或臨時(shí)介質(zhì)文件盗扒。

如果您要將公共文件保存在外部存儲(chǔ)設(shè)備上,請(qǐng)使用 getExternalStoragePublicDirectory() 方法獲取表示外部存儲(chǔ)設(shè)備上相應(yīng)目錄的 File缀去。 該方法使用指定您想要保存以便它們可以與其他公共文件在邏輯上組織在一起的文件類型的參數(shù)侣灶,比如 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;
}

如果您要保存您的應(yīng)用專用文件朵耕,您可以通過調(diào)用 getExternalFilesDir() 并向其傳遞指示您想要的目錄類型的名稱炫隶,從而獲取相應(yīng)的目錄淋叶。通過這種方法創(chuàng)建的各個(gè)目錄將添加至封裝您的應(yīng)用的所有外部存儲(chǔ)文件的父目錄阎曹,當(dāng)用戶卸載您的應(yīng)用時(shí),系統(tǒng)會(huì)刪除這些文件煞檩。

例如处嫌,您可以使用以下方法來創(chuàng)建個(gè)人相冊(cè)的目錄:

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

如果沒有適合您文件的預(yù)定義子目錄名稱,您可以改為調(diào)用 getExternalFilesDir() 并傳遞 null斟湃。這將返回外部存儲(chǔ)上您的應(yīng)用的專用目錄的根目錄熏迹。

切記,getExternalFilesDir() 在用戶卸載您的應(yīng)用時(shí)刪除的目錄內(nèi)創(chuàng)建目錄凝赛。如果您正保存的文件應(yīng)在用戶卸載您的應(yīng)用后仍然可用—比如注暗,當(dāng)您的應(yīng)用是照相機(jī)并且用戶要保留照片時(shí)—您應(yīng)改用 getExternalStoragePublicDirectory()。

無論您對(duì)于共享的文件使用 getExternalStoragePublicDirectory() 還是對(duì)您的應(yīng)用專用文件使用 getExternalFilesDir()墓猎,您使用諸如 DIRECTORY_PICTURES 的 API 常數(shù)提供的目錄名稱非常重要捆昏。這些目錄名稱可確保系統(tǒng)正確處理文件。 例如毙沾,保存在 DIRECTORY_RINGTONES 中的文件由系統(tǒng)媒體掃描程序歸類為鈴聲骗卜,而不是音樂。

查詢可用空間

如果您事先知道您將保存的數(shù)據(jù)量,您可以查出是否有足夠的可用空間寇仓,而無需調(diào)用 getFreeSpace() 或 getTotalSpace() 引起 IOException举户。這些方法分別提供目前的可用空間和存儲(chǔ)卷中的總空間。 此信息也可用來避免填充存儲(chǔ)卷以致超出特定閾值遍烦。

但是俭嘁,系統(tǒng)并不保證您可以寫入與 getFreeSpace() 指示的一樣多的字節(jié)。如果返回的數(shù)字比您要保存的數(shù)據(jù)大小大出幾 MB服猪,或如果文件系統(tǒng)所占空間不到 90%兄淫,則可安全繼續(xù)操作。否則蔓姚,您可能不應(yīng)寫入存儲(chǔ)捕虽。

注:保存您的文件之前,您無需檢查可用空間量坡脐。 您可以嘗試立刻寫入文件泄私,然后在 IOException 出現(xiàn)時(shí)將其捕獲。 如果您不知道所需的確切空間量备闲,您可能需要這樣做晌端。 例如,如果在保存文件之前通過將 PNG 圖像轉(zhuǎn)換成 JPEG 更改了文件的編碼恬砂,您事先將不知道文件的大小咧纠。

刪除文件

您應(yīng)始終刪除不再需要的文件。刪除文件最直接的方法是讓打開的文件參考自行調(diào)用 delete()泻骤。

myFile.delete();
如果文件保存在內(nèi)部存儲(chǔ)中漆羔,您還可以請(qǐng)求 Context 通過調(diào)用 deleteFile() 來定位和刪除文件:

myContext.deleteFile(fileName);
注:當(dāng)用戶卸載您的應(yīng)用時(shí),Android 系統(tǒng)會(huì)刪除以下各項(xiàng):
您保存在內(nèi)部存儲(chǔ)中的所有文件
您使用 getExternalFilesDir() 保存在外部存儲(chǔ)中的所有文件狱掂。
但是演痒,您應(yīng)手動(dòng)刪除使用 getCacheDir() 定期創(chuàng)建的所有緩存文件并且定期刪除不再需要的其他文件。
獲得文件的位置:

文件路徑一覽圖
剩余空間
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末趋惨,一起剝皮案震驚了整個(gè)濱河市鸟顺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌器虾,老刑警劉巖讯嫂,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異兆沙,居然都是意外死亡欧芽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門挤悉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渐裸,“玉大人巫湘,你說我怎么就攤上這事』杈椋” “怎么了尚氛?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)洞渤。 經(jīng)常有香客問我阅嘶,道長(zhǎng),這世上最難降的妖魔是什么载迄? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任讯柔,我火速辦了婚禮,結(jié)果婚禮上护昧,老公的妹妹穿的比我還像新娘魂迄。我一直安慰自己,他們只是感情好惋耙,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布捣炬。 她就那樣靜靜地躺著,像睡著了一般绽榛。 火紅的嫁衣襯著肌膚如雪湿酸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天灭美,我揣著相機(jī)與錄音推溃,去河邊找鬼。 笑死届腐,一個(gè)胖子當(dāng)著我的面吹牛铁坎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梯捕,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼厢呵,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼窝撵!你這毒婦竟也來了傀顾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤碌奉,失蹤者是張志新(化名)和其女友劉穎短曾,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赐劣,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嫉拐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了魁兼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婉徘。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盖呼,到底是詐尸還是另有隱情儒鹿,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布几晤,位于F島的核電站约炎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蟹瘾。R本人自食惡果不足惜圾浅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望憾朴。 院中可真熱鬧狸捕,春花似錦、人聲如沸众雷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽报腔。三九已至株搔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纯蛾,已是汗流浹背纤房。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留翻诉,地道東北人炮姨。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像碰煌,于是被迫代替她去往敵國(guó)和親舒岸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容