Android 7.0 报android.os.FileUriExposedException异常

通过Intent开启相机程序,且指定输出图片路径:

   /**
     * 打开相机
     * @param context
     * @param requestCode
     * @return
     */
    public static void openCamera(Activity context, int requestCode, String picturePath){
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (intent.resolveActivity(context.getPackageManager()) != null) {

            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(picturePath)));

            context.startActivityForResult(intent, requestCode);
        }
    }

运行在Android7.1的模拟器上,结果报一下错误:

  Process: xingencom.easypermissiondemo, PID: 3322
        android.os.FileUriExposedException: 

       file:///storage/emulated/0/Android/data/xingencom.easypermissiondemo/files/Pictures/e3dddc5f1ee3e1db580e8f4b5d68d2ba.png  
       exposed beyond app through ClipData.Item.getUri()

        at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
        at android.net.Uri.checkFileUriExposed(Uri.java:2346)

解释

android 7.0发生了一些行为变化,禁止应用程序向外部公开file://的URI。

尝试传递file://URI会触发FileUriExposedException。

应用程序之间共享数据,应该发送content://的URI,且授予URI临时访问权限。推举使用FileProvider。更多详情,阅读android 7.0行为变更。

解决方式:配置FileProvider


1. 定义访问的图片目录路径

src\main\res路径下创建xml文件夹,然后在创建一个provider_paths.xml文件,编写以下代码:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">

    <files-path name="Pictures" path="/"></files-path>
    <external-path path="Android/data/${applicationId}/" name="files_root" />
    <root-path
        name="root"
        path="/" />
</paths>

因本人这里访问的是context.getExternalFilesDir()context.getFilesDir()目录,所以配置的是<external-path/><files-path/>

创建的相片文件存放路径,代码如下:

  /**
     * 获得存储bitmap的文件
     * getExternalFilesDir()提供的是私有的目录,在app卸载后会被删除
     *
     * @param context
     * @param
     * @return
     */
    public static String getBitmapDiskFile(Context context) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalFilesDir(DIRECTORY_PICTURES).getAbsolutePath();
        } else {
            cachePath =context.getFilesDir().getAbsolutePath();
        }
        return new File(cachePath +File.separator+ getBitmapFileName()).getAbsolutePath();
    }

    public static final String bitmapFormat = ".png";

    /**
     * 生成bitmap的文件名:日期,md5加密
     *
     * @return
     */
    public static String getBitmapFileName() {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            String currentDate = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            mDigest.update(currentDate.getBytes("utf-8"));
            byte[] b = mDigest.digest();
            for (int i = 0; i < b.length; ++i) {
                String hex = Integer.toHexString(0xFF & b[i]);
                if (hex.length() == 1) {
                    stringBuilder.append('0');
                }
                stringBuilder.append(hex);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String fileName = stringBuilder.toString() + bitmapFormat;
        return fileName;
    }

2. 在AndroidManifest.xml中注册FileProvider

为FileProvidre配置,指定authorities,name ,不许对外共享,临时授权,访问目录配置。

     <!-- FileProvider配置访问路径,适配7.0及其以上 -->
        <provider
            android:name="android.support.v4.content.FileProvider"
           android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

3. 使用FileProvider访问指定目录

当系统是7.0及其以上,使用FilProvider访问目录。

 /**
     * 打开相机
     * @param context
     * @param requestCode
     * @return
     */
    public static void openCamera(Activity context, int requestCode, String picturePath){
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (intent.resolveActivity(context.getPackageManager()) != null) {
            /**
             * 指定拍照存储路径
             * 7.0 及其以上使用FileProvider替换'file://'访问
             */
            if (Build.VERSION.SDK_INT>=24){
                //这里的BuildConfig,需要是程序包下BuildConfig。
                intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.APPLICATION_ID+".provider",new File(picturePath)));
                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            }else{
                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(picturePath)));
            }
            context.startActivityForResult(intent, requestCode);
        }
    }

注意点:FileProvider.getUriForFile()中传入authorities和注册的FileProvider带有的authorities一样。


资源参考

猜你喜欢

转载自blog.csdn.net/hexingen/article/details/78505582