android 11.md

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、应用间共享文件

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容