targetSdkVersion = 30
- 分区存储强制执行:应用对文件的读写,只能在沙盒环境也就是应用的专属目录(计入应用所占空间,随应用卸载而删除),其他媒体文件通过MediaStore访问
targetSdkVersion = 29
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:requestLegacyExternalStorage="true"
android:theme="@style/Theme.MyApplication">
targetSdkVersion 覆盖安装可以使用 android:preserveLegacyExternalStorage="true" 暂时关闭
// targetSdkVersion = 30
val filePath1 = Environment.getExternalStoragePublicDirectory("")
val filePath = Environment.getExternalStorageDirectory()
val file = File(filePath,"me.txt")
file.createNewFile()
// 报错:Caused by: java.io.IOException: Permission denied
几种访问文件的方法:
1、应用专属目录
// sdcard == storage/emulated/0 这两个路径相同
// 比如me2.txt 也存在于storage/emulated/0/android/data/packageName/cache/me2.txt
// 以下目录在android 10以下的手机 也可以创建并访问
val filePath = File(filesDir,"me.txt")
filePath.createNewFile()
// 路径:data/data/packageName/files/me.txt
val filePath2 = File(externalCacheDir,"me2.txt")
filePath2.createNewFile()
// 路径:sdcard/android/data/packageName/cache/me2.txt
val filePath3 = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),"me3.txt")
filePath3.createNewFile()
// 路径:sdcard/android/data/packageName/files/download/me3.txt
val filePath4 = File(getExternalFilesDir(Environment.DIRECTORY_DCIM),"dd.jpg")
filePath4.createNewFile()
// 路径:sdcard/android/data/packageName/files/DCIM/dd.jpg
2、访问公共媒体目录文件
private fun intoMediaPath() {
// MediaStore.Images.Media.EXTERNAL_CONTENT_URI 根据Images关键字 假如查找视频,则改为Video
val cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,null,null,null,
"${MediaStore.MediaColumns.DATE_ADDED} desc")
if(cursor != null){
while (cursor.moveToNext()){
val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
val uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,id)
Log.d("cjq","$uri")
if(i == 0){
val fd = contentResolver.openFileDescriptor(uri,"r")
fd?.let {
val bitmap = BitmapFactory.decodeFileDescriptor(it.fileDescriptor) // 图片过大可能OOM
imageView.setImageBitmap(bitmap)
}
}
}
cursor.close()
}
}
3、SAF 存储访问框架( Storage Access FrameWork)
private fun intoMediaFile() {
//android 10 及以下都可用,配置不用的type打开不同文件位置,可配置*/*不做筛选
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
startActivityForResult(intent,100)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(data == null ) return
val uri = data.data
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver,uri) //图片过大会OOM
imageView.setImageBitmap(bitmap)
}
4、下载文件存放外部区域Download目录
private fun downloadFile() {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return
val values = ContentValues()
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "tt.zip")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
val uri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
if (uri != null) {
val outputStream = contentResolver.openOutputStream(uri)
if (outputStream != null) {
outputStream.write(5)
}
}
}
- FileProvider使用场景
1、拍照
private fun capture() {
val takePhotoIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val file = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val imageFile = createTempFile("cjq",".jpg",file)
uri = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
FileProvider.getUriForFile(this,"cn.fonxnickel.officialcamerademo.fileprovider",imageFile) //需和manifest下定义的provider名称一致
}else{
Uri.fromFile(imageFile)
}
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri)
startActivityForResult(takePhotoIntent,10)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != RESULT_OK) return
val bitmap = BitmapFactory.decodeStream(uri?.let { contentResolver.openInputStream(it) })
// val bitmap = MediaStore.Images.Media.getBitmap(contentResolver,uri) //图片过大会OOM
imageView.setImageBitmap(bitmap)
}
在res下新建xml文件夹,并定义filepaths文件
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path
name="files-path"
path="." />
<cache-path
name="cache-path"
path="." />
<external-path
name="external_storage_root"
path="." />
<external-files-path
name="external_file_path"
path="." />
<external-cache-path
name="external_cache_path"
path="." />
<root-path
name="root-path"
path="" />
</paths>
在AndroidManifest下定义provider
<provider
android:authorities="cn.fonxnickel.officialcamerademo.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths"/>
</provider>
2、安装apk
3、应用间共享文件