抬头仰望星空,是否能发现自己的渺小。

伪斜杠青年

人们总是混淆了欲望和理想

Android 10 , 11 调用系统相机错误:Failed to find configured root that contains data.

做H5界面拍照功能遇到的错误:

java.lang.IllegalArgumentException: Failed to find configured root that contains your_file

网上的说法都很旧,大多数是说 FileProvider 配置错误而这里并不是,查看逻辑后发现,之前未适配 Android 10,11 ,所以使用的是 FileProvider.getUriForFile 获取到的URI,然后传递给系统相机即可,但目前新版本并不允许这样做,正确的做法如下:

先根据不同的 Android 版本生成不同的URI:

    val uploadUri:Uri

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val values = ContentValues()
        values.put(MediaStore.Images.Media.DISPLAY_NAME, "pic.jpg")
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        uploadUri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
    } else {
        val authority: String = context.getPackageName().toString() + ".fileprovider"
        val file = File(context.getCacheDir().toString() + "/pic.jpg")
        uploadUri = FileProvider.getUriForFile(context, authority, file)
    }

然后使用该 URI 作为相机拍照后输出的路径:

    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    intent.putExtra("return-data", true)
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uploadUri)
    intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    startActivityForResult(intent, REQUEST_CODE_UPLOAD_IMAGE)

最后在拍照回调里先判断 intent.getData() 是否为空(大概率是为空),为空时直接用 uploadUri 即可。 Uri 怎么用?直接读输入流,然后保存到私有目录:

    InputStream inputStream = getContentResolver().openInputStream(uri);

可以发现整体逻辑变了:

Android 7.0 以下 :自行指定一个私有目录或者其他目录URI,然后传递给系统相机,系统相机获取到权限就可以随意写。

Android 10 及以上:需要先显示在系统中定义一个 URI,先报备再传递给系统相机进行操作,而且目录必须为公用目录(DCIM,Pictures)。

对于 Android 7.0以上的版本只要用 FileProvider 授权就行,Android 7.0以下的版本,随便怎么玩,但在Android 10以上,是不被允许的,同时对于相机的调用,输出目录只能用 DCIM, 以及 Pictures 目录,不信可以将上面的 Environment.DIRECTORY_DCIM 改成其他目录,看看报错就知道了(因为系统应用也没权限写我们应用的私有目录,其他目录我们又没有权限去读取)。

以上。


本站由以下主机服务商提供服务支持:

4条评论

  • ludou

    Android 10 及以上,拍照如何保存到Pictures目录下的指定文件夹,如Pictures/.nomedia。现在发现,删除Pictures根目录下的照片,有时会被华为手机拦截。不删除的话,app拍照产生的临时照片越来越多,占内存。

    MediaStore.Images.Media.EXTERNAL_CONTENT_URI

    这里要怎么改一下?

    • Mosaic-C

      我的理解是:应用自身的 data 目录下的文件不会产生.nomedia,.nomedia 是系统行为,另外 Pictures 文件夹是包含用户其他应用的图片的公共文件夹,属于隐私内容,删除会被拦截是正常行为,合理逻辑应该是:将临时文件保存在应用目录,需要导出的才导出(使用 URI 写入),并且导出后不再进行更改。

      • ludou

        android 10以上系统调用相机拍照不是只能保存到DCIM或者Pictures目录吗?所以拍照的照片没办法保存到应用的cacheDir啊。现在使用RELATIVE_PATH存到Pictures的子目录下,删除照片同样有时会被拦截。如果保存到有.nomedia文件的目录下,又取不到uri。

        • Mosaic-C

          虽然没有尝试,但我感觉 FileProvider.getUriForFile 这个 7.0 加进来的 API 在缓存目录应当是能用的,所以你可以试试,毕竟传递的 Uri 并没有说一定要来自于 MediaStore 创建的。

发表评论