FileProvider 的使用(Failed to find configured root that contains/storage/emulated/0/DCIM/ )

  </div>
  <div class="article-info-box">
    <div class="article-bar-top" style="height: 24px;">
                                              <span class="time">2017年02月26日 11:25:10</span>
      <a class="follow-nickName" href="https://me.csdn.net/leilifengxingmw" target="_blank">leilifengxingmw</a>
      <span class="read-count">阅读数:36268</span><span class="article_info_click" style="position: static;">更多</span>
      
                                  <div class="tags-box space">
            <span class="label">个人分类:</span>
                              <a class="tag-link" href="https://blog.csdn.net/leilifengxingmw/article/category/6061597" target="_blank">菜鸟博客                                  </a>
          </div>
                                      </div>
    <div class="operating">
              </div>
  </div>
</div>
      版权声明:本文为博主原创文章,未经博主允许不得转载。          https://blog.csdn.net/leilifengxingmw/article/details/57405908        </div>
        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
                          <div id="content_views" class="markdown_views prism-atom-one-dark">
        <!-- flowchart 箭头图标 勿删 -->
        <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
          <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
        </svg>
        <p>以前调用系统相机拍照的时候,流程是这样的</p>
 private void takePhoto() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
	        //创建一个路径保存图片
            photoFile = ImageUtil.createImageFile();
            if (photoFile != null) {
                photoURI = Uri.fromFile(photoFile);
                //传递一个Uri
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, TAKE_PHOTO);
            }
        }
    }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

然后在onActivityResult方法中处理拍照结果。

  @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case TAKE_PHOTO:
                if (resultCode == RESULT_OK) {
	                //处理拍照的结果
                    processTakePhoto(photoFile.getPath());
                }
                break;
            default:
                break;
        }
    }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

但是发现在7.0的系统上直接崩溃了,错误如下。
这里写图片描述

android.os.FileUriExposedException: 
file:///storage/emulated/0/Android/data/com.hm.camerademo/files/Picture
s/20170225_140305187933259.jpg exposed beyond app through 
ClipData.Item.getUri()

 
 
  • 1
  • 2
  • 3
  • 4

然后网上搜了一把,是 photoURI = Uri.fromFile(photoFile); 这种创建Uri的方式有问题了,不够安全。需要使用FileProvider来创建Uri.

###使用FileProvider四部曲
第一步,指定一个FileProvider。在AndroidManifest.xml中声明一个条目

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <application
        ...>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.myapp.fileprovider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>
        ...
    </application>
</manifest>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

com.example.myapp是你的包名

第二步,指定想分享的目录。在res目录下新建一个xml目录,在xml目录下面新建一个xml文件。我新建的文件名叫filepaths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
&lt;!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/DCIM/camerademo目录--&gt;
&lt;external-path name="hm_DCIM" path="DCIM/camerademo" /&gt;
&lt;!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/Pictures/camerademo目录--&gt;
&lt;external-path name="hm_Pictures" path="Pictures/camerademo" /&gt;
&lt;!--代表app 私有的存储区域 Context.getFilesDir()目录下的images目录 /data/user/0/com.hm.camerademo/files/images--&gt;
&lt;files-path name="hm_private_files" path="images" /&gt;
&lt;!--代表app 私有的存储区域 Context.getCacheDir()目录下的images目录 /data/user/0/com.hm.camerademo/cache/images--&gt;
&lt;cache-path name="hm_private_cache" path="images" /&gt;
&lt;!--代表app 外部存储区域根目录下的文件 Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)目录下的Pictures目录--&gt;
&lt;!--/storage/emulated/0/Android/data/com.hm.camerademo/files/Pictures--&gt;
&lt;external-files-path name="hm_external_files" path="Pictures" /&gt;
&lt;!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的images目录--&gt;
&lt;!--/storage/emulated/0/Android/data/com.hm.camerademo/cache/images--&gt;
&lt;external-cache-path name="hm_external_cache" path="" /&gt;

</paths>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

name=“name” URI 路径段,取值会隐藏你分享的目录的名字。比如下面这个

  <!--/storage/emulated/0/Android/data/com.hm.camerademo/cache/images-->
    <external-cache-path name="hm_file" path="images" />

 
 
  • 1
  • 2

会用hm_file 替代/storage/emulated/0/Android/data/com.hm.camerademo/cache/images

path=“path” 你分享的目录的名字

###注意
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg错误产生原因。

  <external-path name="hm_DCIM" path="DCIM/camerademo" />
  <external-path name="hm_Pictures" path="Pictures/camerademo" />

 
 
  • 1
  • 2

我可以在 external-path目录下指定多个我想分享的目录,两个分享的目录的name取值不应该相同。我把上面两个的name字段都叫 hm_file,然后看看有什么问题。结果就是会报标题上的那个错误,实验一把

 <external-path name="hm_file" path="DCIM/camerademo" />
 <external-path name="hm_file" path="Pictures/camerademo" />

 
 
  • 1
  • 2

然后我生成一个Content URI。

  File imageFile = null;
        String storagePath;
        File storageDir;
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        try {
		   //文件路径是公共的DCIM目录下的/camerademo目录
           storagePath = Environment
         .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
         .getAbsolutePath() + File.separator + "camerademo";
          storageDir = new File(storagePath);
          storageDir.mkdirs();
          imageFile = File.createTempFile(timeStamp, ".jpg", storageDir);
          Log.e(TAG, imageFile.getAbsolutePath());
        } catch (IOException e) {
          e.printStackTrace();
        }
        return imageFile;

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
//文件路径
/storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg

 
 
  • 1
  • 2
//生成Uri
photoFile = ImageUtil.createImageFile();
photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", photoFile);

 
 
  • 1
  • 2
  • 3

但是报错了。错误如下

 java.lang.IllegalArgumentException: Failed to find configured root 
 that contains 
 /storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg
  • 1
  • 2
  • 3
  • 4

我把上面生成文件的路径改一下

//路径是公共存储路径Pictures目录下的camerademo目录
 storagePath = Environment
 .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
 .getAbsolutePath() + File.separator + "camerademo";
  • 1
  • 2
  • 3
  • 4
  • 5
//生成的文件路径
/storage/emulated/0/Pictures/camerademo/20170226_1104551680202685.jpg
//可以正常生成Uri的路径
/hm_file/20170226_1104551680202685.jpg

 
 
  • 1
  • 2
  • 3
  • 4

上面的问题说明 在filePath.xml 文件中,如果要在同一个存储路径下,指定两个共享的目录,如下所示,那么两个共享路径的name字段取值不应该相同,如果两者相同,那么后面的一行指定的path(/storage/emulated/0/Pictures/camerademo)会覆盖上面一行指定的path(/storage/emulated/0/DCIM/camerademo)

//	共享目录的根目录都是 /storage/emulated/0/
 <external-path name="hm_file" path="DCIM/camerademo" />
 <external-path name="hm_file" path="Pictures/camerademo" />

 
 
  • 1
  • 2
  • 3

###第三步 为一个文件生成 Content URI

   File imageFile = null;
   String storagePath;
   File storageDir;
   String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        try {
            storagePath = App.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();
            storageDir = new File(storagePath);
            storageDir.mkdirs();
            imageFile = File.createTempFile(timeStamp, ".jpg", storageDir);
        } catch (IOException e) {
            e.printStackTrace();
        }
//创建Uri
Uri  photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", imageFile );

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
com.hm.camerademo.fileprovider

 
 
  • 1

要和在AndroidManifest.xml中指定的一样。不然会报错。

###第四步 分享一个 Content URI
这个例子中我们是向系统的相机传递一个Uri

photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", photoFile);
Log.e(TAG, photoURI.getPath());
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, TAKE_PHOTO);

 
 
  • 1
  • 2
  • 3
  • 4

最后附上两张图,图片来自参考文档2

图一:使用Uri.fromFile()的方式生成一个Uri
这里写图片描述

图一:使用FileProvider.getUriForFile(this, “com.hm.camerademo.fileprovider”, photoFile);的方式生成一个Uri

这里写图片描述

参考文档
【1】https://developer.android.com/training/secure-file-sharing/setup-sharing.html
【2】https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en

      </div>
      <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-258a4616f7.css" rel="stylesheet">
              </div>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值