內(nèi)部存儲
內(nèi)部存儲主要用于保存應(yīng)用的私有文件,其他應(yīng)用無法訪問這些數(shù)據(jù)。當應(yīng)用卸載的時候,這些數(shù)據(jù)也會被刪除。使用內(nèi)部存儲不需要任何額外權(quán)限。
1.寫入數(shù)據(jù)
FileOutputStream outputStream = context.openFileOutput("filePath", Context.MODE_PRIVATE);
2.讀取數(shù)據(jù)
FileInputStream inputStream = context.openFileInput("filePath");
3.讀取讀取靜態(tài)文件
InputStream inputStream = context.getResources().openRawResource(R.raw.rawfile) ;
注意,在raw文件夾中,文件名只能包含小寫字母、數(shù)字和下劃線。
4.緩存數(shù)據(jù)
File cacheFile = context.getCacheDir();
這個File對象對應(yīng)的就是內(nèi)部存儲中用于保存緩存數(shù)據(jù)的根目錄。
注意,應(yīng)用的私有緩存文件不應(yīng)該過大。如果內(nèi)部存儲空間不足,系統(tǒng)可能會刪除這些緩存文件。為了保證良好的用戶體驗,應(yīng)用應(yīng)該定期主動清除自己的緩存數(shù)據(jù)。
外部存儲
除了內(nèi)部存儲,Android系統(tǒng)還為開發(fā)者提供了外部存儲。外部存儲并不僅僅指SD卡,它可能是可移除的存儲介質(zhì)(典型如SD卡),也可能是不可移除的存儲介質(zhì)(如現(xiàn)在很多一體機內(nèi)置的存儲器)。外部存儲是相對于內(nèi)部存儲的概念,用于保存全局范圍可讀取的文件。這也就意味著,保存在外部存儲中的數(shù)據(jù)可以被設(shè)備中的任何應(yīng)用訪問,甚至也可以被用戶查看、修改。
1.申請權(quán)限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
如果應(yīng)用同時有讀、寫的需求,只需要申請WRITE_EXTERNAL_STORAGE權(quán)限即可。
注意,Android 6.0(API 23、Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)引入了運行時權(quán)限的概念,以上提到的兩種權(quán)限都需要動態(tài)地獲取。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
在Android 4.4(API 19)及以上,如果只是在外部存儲中讀、寫應(yīng)用的私有文件,就不需要申請這些權(quán)限。因此,我們可以使用maxSdkVersion屬性實現(xiàn)只在較低版本申請權(quán)限,如上所示。
2.Environment中定義的外部存儲狀態(tài)常量:
MEDIA_UNKNOWN:未知狀態(tài)
MEDIA_REMOVED:移除狀態(tài)(外部存儲不存在)
MEDIA_UNMOUNTED:未裝載狀態(tài)(外部存儲存在但是沒有裝載)
MEDIA_CHECKING:磁盤檢測狀態(tài)
MEDIA_NOFS:外部存儲存在,但是磁盤為空或使用了不支持的文件系統(tǒng)
MEDIA_MOUNTED:就緒狀態(tài)(可讀、可寫)
MEDIA_MOUNTED_READ_ONLY:只讀狀態(tài)
MEDIA_SHARED:共享狀態(tài)(外部存儲存在且正通過USB共享數(shù)據(jù))
MEDIA_BAD_REMOVAL:異常移除狀態(tài)(外部存儲還沒有正確卸載就被移除了)
MEDIA_UNMOUNTABLE:不可裝載狀態(tài)(外部存儲存在但是無法被裝載,一般是磁盤的文件系統(tǒng)損壞造成的)
3.外部存儲可寫、可讀
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
4.外部存儲至少可讀
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
公共文件(共享文件)
對于在應(yīng)用中產(chǎn)生的多媒體類型的文件,如音樂、圖片、鈴聲等,一般應(yīng)該保存在外置存儲中對應(yīng)的公共目錄下,如/Music、/Pictures、/Ringtones,這樣方便和其他的應(yīng)用共享這些文件。同時,系統(tǒng)的媒體掃描器也能正確地對這些文件進行歸類。
1.Environment中定義文件類型的常量:
DIRECTORY_MUSIC:音樂類型
DIRECTORY_PICTURES:圖片類型
DIRECTORY_MOVIES:電影類型
DIRECTORY_DCIM:照片類型
DIRECTORY_DOWNLOADS:下載文件類型
DIRECTORY_DOCUMENTS:文檔類型
DIRECTORY_RINGTONES:鈴聲類型
DIRECTORY_ALARMS:鬧鐘提示音類型
DIRECTORY_NOTIFICATIONS:通知提示音類型
DIRECTORY_PODCASTS:播客音頻類型
2.獲取共享文件目錄
File file = Environment.getExternalStoragePublicDirectory(String type);
注意,返回的文件目錄可能還不存在,因此在執(zhí)行文件操作前應(yīng)該確保相應(yīng)的文件目錄已經(jīng)存在,否則使用File的mkdirs方法創(chuàng)建文件目錄。
小技巧:如果不希望系統(tǒng)的媒體掃描器訪問我們的媒體文件,可以在媒體文件所在的目錄下新建一個名為.nomedia的空文件,這會阻止媒體掃描器歸類我們的文件并提供給其他應(yīng)用。
私有文件
對于應(yīng)用私有的文件,則應(yīng)該使用Context的getExternalFilesDir方法訪問外部存儲中的私有存儲目錄,媒體掃描器不會掃描這些目錄。可以為這個方法傳入一個String類型的type參數(shù),用于獲取私有存儲目錄中相應(yīng)的媒體文件子目錄。當然,也可以傳入null直接獲取私有存儲的根目錄。這個方法的返回值也是一個File對象。
1.Environment中定義文件類型的常量:
DIRECTORY_MUSIC:音樂類型
DIRECTORY_PICTURES:圖片類型
DIRECTORY_MOVIES:電影類型
DIRECTORY_RINGTONES:鈴聲類型
DIRECTORY_ALARMS:鬧鐘提示音類型
DIRECTORY_NOTIFICATIONS:通知提示音類型
DIRECTORY_PODCASTS:播客音頻類型
另外,出于兼容性的考慮,可以使用ContextCompat的getExternalFilesDirs方法。這是一個靜態(tài)方法,返回值也是一個File數(shù)組。在Android 4.4及以上,效果和Context的getExternalFilesDirs方法一致;而在Android 4.3及以下,返回的File數(shù)組始終只包含一個對象。
注意,某些移動設(shè)備可能既提供了內(nèi)置存儲器作為外部存儲空間,同時又提供了SD卡作為外部存儲空間。也就是說,在這些設(shè)備中外部存儲實際上包含了兩塊磁盤。在Android 4.3(API 18)及以下,Context的getExternalFilesDir方法僅僅會返回內(nèi)置存儲器對應(yīng)的外部存儲控件,而無法訪問SD卡對應(yīng)的存儲空間。從Android 4.4(API 19)開始,Context新增了getExternalFilesDirs方法。這個方法的返回值是一個File數(shù)組,包含兩個對象(可能為null),這樣就可以實現(xiàn)對內(nèi)置存儲器和SD卡的訪問。數(shù)組的第一個對象默認是外部主存儲,官方的開發(fā)建議是除非這個位置已滿或不可用,否則應(yīng)該使用這個位置。
注意,當應(yīng)用卸載時,這些私有存儲目錄中的文件也會被刪除。此外,雖然系統(tǒng)的媒體掃描器不會訪問外部存儲中的私有存儲目錄,但是其他具有READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE權(quán)限的應(yīng)用依舊可以讀/寫這些私有存儲目錄中的文件。因此對于真正重要的文件,還是應(yīng)該保存在應(yīng)用的內(nèi)部存儲中。
補充:私有文件根目錄的參考路徑:Android/data/包名/files/
緩存文件
在外部存儲中也有專門保存緩存文件的空間,可以通過Context的getExternalCacheDir方法訪問緩存文件目錄,返回值是一個File對象。上文曾說過,外部存儲可能同時包含內(nèi)置存儲器和SD卡兩個存儲空間,因此在Android 4.4(API 19)及以上還可以通過Context的getExternalCacheDirs方法訪問這兩個存儲空間。這個方法會返回一個File數(shù)組,包含兩個對象,第一個對象默認是外部主存儲對應(yīng)的緩存文件目錄。
同樣,為了兼容性,也可以使用ContextCompat的getExternalCacheDirs方法。這是一個靜態(tài)方法,返回值也是一個File數(shù)組。在Android 4.4及以上,效果和Context的getExternalCacheDirs方法一致;而在Android 4.3及以下,返回的File數(shù)組始終只包含一個對象。
注意,當應(yīng)用卸載時,緩存目錄下的文件也會被系統(tǒng)刪除。當然,官方建議開發(fā)者應(yīng)該主動移除不再需要的緩存文件,這有助于節(jié)省存儲空間并保持應(yīng)用性能。
補充:緩存文件根目錄的參考路徑:Android/data/包名/cache/