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)的異常處理.
刪除文件
文件刪除的方法有兩種:
- file.delete(). 直接調(diào)用File的delete()方法.
- 如果文件是存放在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()中的文件,以及其他的無用文件.