android的物理存儲(chǔ)劃分
安卓設(shè)備的物理存儲(chǔ) 分為兩大塊淑趾,內(nèi)部存儲(chǔ)和外部存儲(chǔ)
- 內(nèi)部存儲(chǔ)
設(shè)備中每一個(gè)安裝的 App莉炉,系統(tǒng)都會(huì)在內(nèi)部存儲(chǔ)空間的 data/data 目錄下以應(yīng)用包名為名字自動(dòng)創(chuàng)建與之對(duì)應(yīng)的文件夾梳虽,這個(gè)文件夾也用來(lái) 存放SharedPreferences 和 SQLiteDatabase 的數(shù)據(jù)假瞬, App 中的 WebView 緩存頁(yè)面信息也在這文件夾下义郑;
但是 當(dāng)app被卸載的時(shí)候肝断,這個(gè)文件夾 會(huì)被刪除掉杈曲。
開發(fā)過(guò)程中,可通過(guò) Context對(duì)象提供的 API 讀取操作 內(nèi)部存儲(chǔ)中的文件
//內(nèi)部存儲(chǔ)
/**
* Environment.getDataDirectory() : /data
* context.getFilesDir() : /data/user/0/com.e.dk_wd/files
* context.getCacheDir() : /data/user/0/com.e.dk_wd/cache
* context.getDataDir() : /data/user/0/com.e.dk_wd
*/
Log.i("----", "Environment.getDataDirectory() : " + Environment.getDataDirectory().absolutePath)
Log.i("----", "context.getFilesDir() : " + this.filesDir!!.absolutePath)
Log.i("----", "context.getCacheDir() : " + this.cacheDir!!.absolutePath)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Log.i("----", "context.getDataDir() : " + this.dataDir!!.absolutePath)
}
- 外部存儲(chǔ)
一般的手機(jī)設(shè)備都有會(huì)內(nèi)置 SD 卡胸懈,同時(shí)也提供 SD 卡的拓展担扑,可能對(duì)應(yīng)路徑的目錄名有所差異。
所說(shuō)的外部存儲(chǔ)趣钱,就是手機(jī)設(shè)備內(nèi)置的SD卡和擴(kuò)展的SD卡 提供的存儲(chǔ)空間涌献;
外部存儲(chǔ) 也會(huì)為 安裝的app 提供一塊區(qū)域(文件夾) ,用來(lái)存放私有的文件
通常的路徑是:/storage/emulated/0/Android/data/包名/
什么是存儲(chǔ)分區(qū)
存儲(chǔ)分區(qū)首有,是android系統(tǒng)對(duì)APP訪問(wèn)外部存儲(chǔ) 添加了限制燕垃;開啟存儲(chǔ)分區(qū)后 APP只能訪問(wèn)自己目錄下的文件和公共文件,
需要特別指出的是android 10 雖支持存儲(chǔ)分區(qū) 但可不開啟井联,對(duì)于android11 來(lái)說(shuō)卜壕,必須開啟存儲(chǔ)分區(qū),android11必須使用存儲(chǔ)分區(qū)烙常,
即使設(shè)置 android:requestLegacyExternalStorage="true" 也無(wú)效
//android10(包括android10)以下 可在清單文件中聲明 android:requestLegacyExternalStorage="true" 來(lái)不啟用存儲(chǔ)分區(qū)
<application
...
android:requestLegacyExternalStorage="true">
開啟存儲(chǔ)分區(qū)后對(duì)文件訪問(wèn)的影響
對(duì)原來(lái)的內(nèi)部存儲(chǔ)沒有什么影響轴捎,但是對(duì)外部存儲(chǔ)有影響;
外部 存儲(chǔ)有兩個(gè)區(qū)域 app私有區(qū)域和app共享區(qū)域蚕脏;
- app私有區(qū)域
文件目錄在/android/data/app包名 下侦副,APP自身 不需要讀寫權(quán)限 就可以進(jìn)行讀寫,其他的app是無(wú)法訪問(wèn)這一區(qū)域的(自android7.0起)蝗锥;開啟存儲(chǔ)分區(qū)后 無(wú)變化 - app共享區(qū)域
只要有讀寫權(quán)限 每個(gè)app都可以讀寫跃洛,文件目錄有DCIM、Pictures终议,Download等在/storage/emulated/0 下的文件夾及文件;開啟存儲(chǔ)分區(qū)后汇竭,無(wú)法直接通過(guò)File 訪問(wèn)共享區(qū)域的文件,需要使用FileProvider或者M(jìn)ediaStore 來(lái)進(jìn)行訪問(wèn)穴张。
例如:在android 10以下的手機(jī)設(shè)備上 調(diào)起相機(jī)拍照细燎,使用 Uri.fromFile 的方式來(lái)創(chuàng)建照片文件是 沒有問(wèn)題的,但是 android11上皂甘,即使調(diào)起了相機(jī)拍照后 也無(wú)法成功保存照片
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//android7開始 就支持FileProvider,所以這里判斷大于android7
mCurrentPhotoUri = FileProvider.getUriForFile(mContext,
"com.xx.fileprovider", photoFile);
} else {
mCurrentPhotoUri = Uri.fromFile(photoFile);
}
什么是FileProvider?
android 7 后支持 應(yīng)用之間共享文件玻驻,這就是FileProvider的作用;
使用FileProvider 需要在清單文件中 聲明,注意一下 android:authorities指定的屬性值璧瞬,盡可能的保證唯一性 一般以".fileProvider" 結(jié)尾
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
<!--這個(gè)file_paths文件在 Resource/xml 下-->
android:resource="@xml/file_paths"
tools:replace="android:resource" />
</provider>
其中 file_paths文件在 Resource/xml 下,原來(lái)指定共享的 文件路徑户辫;
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="file_path"
path="." />
<cache-path
name="cache_path"
path="." />
<external-path
name="external_path"
path="." />
<external-files-path
name="external_files_path"
path="." />
<external-cache-path
name="external_cache_path"
path="." />
<!--
每個(gè)節(jié)點(diǎn)都支持兩個(gè)屬性:name+path;
//path:需要臨時(shí)授權(quán)訪問(wèn)的路徑(.代表所有路徑)
//name:就是你給這個(gè)訪問(wèn)路徑起個(gè)名字
例如:
<root-path name="root" path="" /> //代表設(shè)備的根目錄new File("/");
<files-path name="files" path="" /> //context.getFilesDir()
<cache-path name="cache" path="" /> //context.getCacheDir()
<external-path name="external" path="" /> //Environment.getExternalStorageDirectory()
<external-files-path name="name" path="path" /> //context.getExternalFilesDirs()
<external-cache-path name="name" path="path" /> //getExternalCacheDirs()
-->
</paths>
然后使用FileProvider API 來(lái)生成File的Uri路徑;
FileProvider.getUriForFile(mContext,"com.xx.fileprovider", photoFile);
這個(gè)"com.xx.fileprovider" 就是 授權(quán)認(rèn)證的信息嗤锉,如果填寫的和清單文件中的不一致 會(huì) 導(dǎo)致文件讀寫時(shí) 錯(cuò)誤渔欢;