文件存儲

Internal和External Storage

所有的Android設(shè)備都有兩個文件存儲區(qū)域:internal storage和 external storage. 這兩個名字是由于早期的Android設(shè)備是由一個內(nèi)置不可變的internal storage和一個可移除的存儲媒介(external storage,比如SD卡)組成的. 現(xiàn)在有的設(shè)備只有一個存儲區(qū)域,但是還是會把它分成internal和external兩個分區(qū),這樣就不管什么設(shè)備都會統(tǒng)一有兩個分區(qū),API的行為也就跟是否有可移除的存儲媒介無關(guān)了.這兩總分區(qū)的特點總結(jié)如下:

區(qū)別 Internal storage External storage
可見性 一直可見 不總是可見,mount了就可見,remove了就不可見
訪問權(quán)限 保存在這里的文件默認只有相應(yīng)的app才能訪問 誰都能讀
卸載時表現(xiàn) 保存在此的與app相關(guān)的文件會被刪除 保存在這里的文件除了在getExternalFilesDir()getExternalCacheDir()文件夾之外,都會保留
是否需要permission 無需
適用條件 你要存放的數(shù)據(jù)只有你的app能訪問,其他的用戶和app都不能訪問 存放的數(shù)據(jù)不需要訪問限制,為了分享數(shù)據(jù)或允許其他用戶訪問

Tip: 盡管app默認是安裝在internal storage上,但是你還是可以通過設(shè)置 android:installLocation 來引導(dǎo)app優(yōu)先安裝位置,如果的你的app體積較大時用這種方式比較好.

External Storage的Permissions的獲取

對external storage的寫入需要在manifest中聲明請求WRITE_EXTERNAL_STORAGE權(quán)限:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

相對于寫入權(quán)限,還有一個讀取權(quán)限READ_EXTERNAL_STORAGE,但是這個權(quán)限有些需要注意:

  • 當(dāng)你請求了WRITE_EXTERNAL_STORAGE權(quán)限時,默認也有了READ_EXTERNAL_STORAGE的權(quán)限.
  • API 19以前,READ_EXTERNAL_STORAGE不是強制的,所有app默認都有訪問external storage的權(quán)限, 但是API 19及之后就需要.
  • 從API 19開始, external storage中的兩個文件夾getExternalFilesDir() 和getExternalCacheDir()中的文件的讀寫權(quán)限將不在需要.

總的來說, 如果只在getExternalFilesDir()和getExternalCacheDir()中進行讀寫,不需要請求權(quán)限;如果在external storage的其他部分進行讀寫,就請求相應(yīng)的讀寫權(quán)限.

Internal Storage中文件的存儲

在internal storage中,你可以使用這兩個方法來獲取相關(guān)路徑:

  • getFilesDir()
  • getCacheDir(),存放app的臨時緩存文件,記得不用時要刪除這些文件,并且給這個文件設(shè)置一個合理的大小比如1M,因為如果系統(tǒng)內(nèi)存緊張時,這些文件可能會在沒有任何警告的情況下被刪除.

這兩個方法獲取的是與本app相關(guān)文件存放的路徑,拿到之后可以在里面進行存儲操作:

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

還可以直接調(diào)用openFileOutput()(openFileOutput只是在封裝了getFilesDir()再加一些操作)來進行寫入操作:

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)該要用File類的createTempFile()方法,如下例:

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存放在internal storage中的文件是以app的包名為路徑存放在Android系統(tǒng)的一個特殊位置.嚴格意義上來看, 其他app是無法訪問你的存儲的路徑,除非你設(shè)置了這些文件的Mode為可讀或可寫. 也就是如果你使用MODE_PRIVATE來設(shè)置你存放在internal storage中的文件,其他的app將無法訪問.

External Storage中文件的存儲

由于external storage可能是無法訪問的,因為SD卡被移除等原因,所有你每次訪問時都要判斷它是否可以訪問. 可以使用getExternalStorageState()獲取external storage的狀態(tài)來做判斷,如下:

/* 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都可以訪問external storage,但是其中可以分為兩類:

  • 公共文件: 數(shù)據(jù)開放給所有app和用戶,app卸載之后數(shù)據(jù)也會保留,比如下載的圖片.
  • 私有文件: 相應(yīng)app專用的數(shù)據(jù),app卸載之后數(shù)據(jù)會被刪除,數(shù)據(jù)對其他的用戶或app沒有意義,比如臨時文件.

如果你想要將存放公共文件在external storage中,可以用Environment類中的getExternalStoragePublicDirectory(String type)方法,傳入一個type參數(shù)來獲取合適的路徑,比如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;
}

如果要存放私有文件到external storage,可以調(diào)用getExternalFilesDir(),并傳入一個type值指定相應(yīng)的路徑:

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

如果沒有合適的type,也可以傳入null,會返回app在external storage的私有路徑.

總之記住,私有文件用getExternalFilesDir(),公共文件用getExternalStoragePublicDirectory(),但是type值盡量用API提供的常量值,如DIRECTORY_PICTURES,這樣才能保證系統(tǒng)會正確處理這些文件,比如存放在DIRECTORY_RINGTONES類型的文件中文件會被識別為鈴聲.

查詢空余空間

如果提前知道要存儲的數(shù)據(jù)的大小,你就可以去看看系統(tǒng)是否有足夠的空間來存放而不會拋出IOException. 調(diào)用File類的兩個方法getFreeSpace()getTotalSpace().

然而, 系統(tǒng)并不能保證你可以使用全部getFreeSpace()中所示的大小,如果freeSpace很多的話那當(dāng)然可以直接寫,如果很小的話則需要注意.

注意: 如果你不知道要存儲的數(shù)據(jù)的大小,你就不用去查看系統(tǒng)剩余的可用空間,你可以直接進行讀寫然后catch一個IOExcetion來做相應(yīng)的異常處理.

刪除文件

文件刪除的方法有兩種:

  1. file.delete(). 直接調(diào)用File的delete()方法.
  2. 如果文件是存放在internal storage中,則可以用Context的deleteFile()方法.
// 1. delete()
file.delete();
-------------------------
// 2. deleteFile()
mContext.deleteFile(fileName);

注意在app被卸載時,下列數(shù)據(jù)會被刪除:

  • internal storage中的所有相關(guān)數(shù)據(jù).
  • external storage中的所有私有文件.

還有,你應(yīng)該手動刪除getCacheDir()中的文件,以及其他的無用文件.

Reference

  1. Saving Files
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市椅您,隨后出現(xiàn)的幾起案子掀泳,更是在濱河造成了極大的恐慌员舵,老刑警劉巖马僻,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件措近,死亡現(xiàn)場離奇詭異瞭郑,居然都是意外死亡鸭你,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俺夕,“玉大人劝贸,你說我怎么就攤上這事映九〖” “怎么了引有?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵譬正,是天一觀的道長曾我。 經(jīng)常有香客問我您单,道長虐秦,這世上最難降的妖魔是什么悦陋? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任幸逆,我火速辦了婚禮还绘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拍顷。我一直安慰自己昔案,他們只是感情好电媳,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布匾乓。 她就那樣靜靜地躺著钝尸,像睡著了一般珍促。 火紅的嫁衣襯著肌膚如雪猪叙。 梳的紋絲不亂的頭發(fā)上穴翩,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天歉嗓,我揣著相機與錄音鉴分,去河邊找鬼志珍。 笑死伦糯,一個胖子當(dāng)著我的面吹牛敛纲,可吹牛的內(nèi)容都是我干的淤翔。 我是一名探鬼主播办铡,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼童叠!你這毒婦竟也來了厦坛?” 一聲冷哼從身側(cè)響起杜秸,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呢蛤,沒想到半個月后其障,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體励翼,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡扶认,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年辐宾,在試婚紗的時候發(fā)現(xiàn)自己被綠了叠纹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡持偏,死狀恐怖鸿秆,靈堂內(nèi)的尸體忽然破棺而出卿叽,到底是詐尸還是另有隱情考婴,我是刑警寧澤沥阱,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站奔则,受9級特大地震影響易茬,放射性物質(zhì)發(fā)生泄漏抽莱。R本人自食惡果不足惜食铐,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一象泵、第九天 我趴在偏房一處隱蔽的房頂上張望偶惠。 院中可真熱鬧忽孽,春花似錦兄一、人聲如沸瘾腰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽召廷。三九已至竞慢,卻和暖如春筹煮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背准脂。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留湾戳,地道東北人院塞。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像汹族,于是被迫代替她去往敵國和親顶瞒。 傳聞我的和親對象是個殘疾皇子榴徐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

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