Android 文件存儲淺談

圖片來源于https://blog.csdn.net/wangsen927/article/details/115914821
1.內部存儲
1.1內部存儲簡單認識
內部存儲一般指data/data/包名/... 下的路徑

有些人經常把內部存儲和運行內存搞混,這完全是兩個截然不同的東西。
運行內存(RAM(Random Access Memory))。用于存儲應用運行時的各種對象和變量常量等,主要作用在于提高運行速度。是唯一一種斷電后數據會清除的存儲器。
(Read-Only Memory,ROM)。電源切斷文件依然保留,PC端的硬盤和手機端文件存儲都屬于ROM。
假如一臺Android機器擁有8G運行內存(RAM),128G手機存儲(ROM)。那么8G就是運行內存大小,128G就是手機內部存儲和外部存儲加起來的總空間大小。
1.2 內部存儲常用API
對于每個應用,系統都會在內部存儲空間中提供目錄,應用可以在該存儲空間中整理其文件。一個目錄專為應用的持久性文件而設計,而另一個目錄包含應用的緩存文件。您的應用不需要任何系統權限即可讀取和寫入這些目錄中的文件。
內部存儲常用的兩個文件目錄可分為持久化文件目錄和緩存文件目錄。
1.2.1 持久化文件目錄
持久化文件目錄下文件訪問
File file = new File(context.getFilesDir(), filename);
持久化文件目錄下文件的寫入
String filename = "myfile";
String fileContents = "Hello world!";
try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) {
fos.write(fileContents.toByteArray());
}
如需允許其他應用訪問存儲在內部存儲空間內此目錄中的文件,請使用具有 FLAG_GRANT_READ_URI_PERMISSION 屬性的 FileProvider。
getCacheDir() 方法
IO流讀取持久化目錄文件
FileInputStream fis = context.openFileInput(filename);
InputStreamReader inputStreamReader =
new InputStreamReader(fis, StandardCharsets.UTF_8);
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
String line = reader.readLine();
while (line != null) {
stringBuilder.append(line).append('\n');
line = reader.readLine();
}
} catch (IOException e) {
// Error occurred when opening raw file for reading.
} finally {
String contents = stringBuilder.toString();
}
注意:如果在安裝時需要以信息流的形式訪問文件,請將文件保存在項目的 /res/raw 目錄中。您可以使用 openRawResource() 打開這些文件,傳入帶有 R.raw 前綴的文件名作為資源 ID。此方法將返回一個 InputStream,您可以使用它讀取文件。您無法寫入原始文件
1.2.2 內部緩存文件目錄
創建緩存文件
File.createTempFile(filename, null, context.getCacheDir());
訪問緩存文件
File cacheFile = new File(context.getCacheDir(), filename);
注意:當設備的內部存儲空間不足時,Android 可能會刪除這些緩存文件以回收空間。因此,請在讀取前檢查緩存文件是否存在。
2.外部存儲
2.1外部存儲簡單認識
外部存儲指的是storage/emulated/0 下的目錄文件。下面我們從外部存儲的路徑和權限來了解一下外部存儲。

如上圖:
標注①處的路徑和標注②處是同一路徑。
②路徑下的目錄文件①下面同樣有一份,但是并不是復制了一份,而是相當于軟鏈接(可以把①處的路徑當作②處路徑的別名)。
外部存儲可分為外部私有目錄和外部公有目錄。
③就是外部私有目錄的路徑。storage/emulated/0/Android/data/包名,其它可以稱作是外部存儲的公有目錄。
1)Android4.4以前需要添加以下權限才能對外部存儲進行讀寫操作。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2)Android4.4 - Android 6.0 不需要添加權限就能對外部存儲的私有目錄進行操作,對外部存儲的公有目錄還是必須申請。
(就是說如果我們自身的應用假如包名為com.wind.storage,那么在storage/emulated/0/Android/data/com/wind/storage/下的文件在4.4-6.0的系統上我們不必申請權限就可以訪問。假如在我們的應用中想要訪問其它應用私有目錄下的文件或者公有目錄下的文件,需要加上讀寫權限.)
3)Android6.0以上,訪問外部存儲下的目錄文件時,不僅需要在清單文件中聲明讀寫權限,還要對讀寫權限進行動態申請。訪問自己應用私有目錄下的文件不需要權限申請。
申請動態存儲權限
private void requestPermission() {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
List<String> permissionsToRequire = new ArrayList<>();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissionsToRequire.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissionsToRequire.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if (!permissionsToRequire.isEmpty()) {
ActivityCompat.requestPermissions(this, mPermissions, 0);
}
}
}
}
根據是否成功獲取權限做出相應的動作,這里如果獲取到權限就什么都不做,獲取失敗退出。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 0) {
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "You must allow all the permissions.", Toast.LENGTH_SHORT).show();
finish();
}
}
}
}
4)Android10以上,Android采用了分區存儲機制。應用只能訪問自己的外部存儲私有目錄以及外部存儲公有目錄下的文件,而不能訪問別的應用的外部存儲私有目錄。
目標SDK設置Android10以下時,/sdcard/Android/data/包名/ 路徑下的文件在Android10上不同應用可以相互訪問,在Android11上不能相互訪問,會報錯。
Android 6.0之前如果要訪問外部存儲目錄下的文件只需要在清單文件上加上讀寫權限
Android 6.0之后如果要想訪問外部存儲目錄下的文件不僅要在清單文件上加上權限,還要動態申請存儲權限(Android6.0開始谷歌要求所有危險權限要動態申請,外部存儲的讀寫權限屬于危險權限)。
分區存儲機制大大提高了Android存儲的安全。有些開發者對外部存儲的一些概念含糊不清,往往把應用本身的一些隱私數據放在外部存儲的私有目錄下面。而其它應用只需要獲取申請讀寫權限,就可以隨意讀取這些隱私數據。分區存儲完美地避免了此種情形。
2.2 外部存儲常用API
2.2.1 創建或訪問外部私有目錄持久化文件
File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);
2.2.2 創建或訪問外部私有目錄緩存文件
File externalCacheFile = new File(context.getExternalCacheDir(), filename);
Android應用卸載時應用內部存儲的私有目錄和應用外部存儲的私有目錄下的文件都會被刪除。
2.3 共享存儲
外部存儲公有目錄的簡單認識
Android 10之前可以直接用new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)+"/"+"文件名")這種方式來訪問外部存儲的公有目錄。
從Android10開始由于開啟了作用域存儲不再支持以這種通過絕對路徑的方式來訪問外部存儲的公有目錄,Android系統針對文件類型進行了分類,圖片、音頻、視頻這三類文件將可以通過MediaStore API來進行訪問,而其他類型的文件則需要使用系統的文件選擇器來進行訪問。
具體關于作用域存儲的適配:例如下載文件到外部存儲公有目錄,添加圖片到圖庫等可參考博客:https://guolin.blog.csdn.net/article/details/105419420
參閱:https://developer.android.com/training/data-storage?hl=zh-cn

浙公網安備 33010602011771號