Android 7.0 通过FileProvider共享文件

一.概述

Android 7.0后,提供了很多新特性,其中最主要的是禁止了通过file://URI直接在文件操作共享文件(该操作会触发FileUriExposedException),而是通过content://URI来实现共享。

FileRrovider是ContentProvider的子类,用于不同应用间的文件共享。

二 使用

1.在Mainfest文件中声明provider。

 <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
 </provider>

2.编写资源文件provider_paths。

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

    <files-path
        name="files"
        path=""/>

    <cache-path
        name="cache"
        path=""/>

    <external-path
        name="external"
        path=""/>

    <external-files-path
        name="external-file"
        path=""/>

    <external-cache-path
        name="external-cache"
        path=""/>
</paths>

在paths节点内部支持以下几个子节点,分别为:

  • <root-path/> 代表设备的根目录new File("/");
  • <files-path/> 代表context.getFilesDir()
  • <cache-path/> 代表context.getCacheDir()
  • <external-path/> 代表Environment.getExternalStorageDirectory()
  • <external-files-path>代表context.getExternalFilesDirs()
  • <external-cache-path>代表getExternalCacheDirs()

path代表目录下的子目录,如:

<cache-path
    name = "cache"
    path = "path
/>

代表context.getChcheDir()/path目录,如果path为空,代表直接使用该根目录。

既然要使用content://URI替代file://URI,那么我们需要一个虚拟路径对真实文件路径进行映射。通过编写xml文件,其中path路径确定了可访问的文件目录,name属性映射了真实文件路径。

3.使用fileProvider API 安装APK

我们一般编写安装APK操作时,是这么写的。

  private void installAPK() {
        File file = new File(Environment.getExternalStorageDirectory(),"test.apk");

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
        startActivity(intent);

    }

拿着在7.0上的手机跑一下,果不其然会报android.os.FileUriExposedException。

简单修改下URI的获取方式。

    private void installAPK() {
        File file = new File(Environment.getExternalStorageDirectory(),"test.apk");

        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri fileUri = null;
        if(Build.VERSION.SDK_INT >= 24){
            //输出 content://com.example.fanggao.fgtextdemo/external/test.apk
            fileUri = FileProvider.getUriForFile(this,BuildConfig.APPLICATION_ID+".fileProvider",file);
        }else {
           fileUri = Uri.fromFile(file);
        }
        intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        startActivity(intent);

    }

这样就可以正常运行了。注意,我们还需要加上权限,否则会报Permission Denial异常。

那么问题来了,当我们直接使用fileProvider时,在5.0以下的手机也会报Permission Denial异常,是不是也可以使用addFlags()方法添加权限呢?

答案是不行的,但addFlags只是使用与setData,setDataandType以及setClipData,而且该方法在5.0以下是无效的,需要使用grantUrlPermission()方法,获取所有符合授权的应用,全部授权。

三 小结

7.0后使用fileProvider来实现文件共享,主要目的是隐藏真实的文件目录,因为fileProvider是ContentProvider的子类,所以需要在AndroidManifest.xml文件中注册;并且需要编写xml文件描述可使用的文件夹目录,通过name 去映射文件真实目录,实现访问的安全性。

另外获取授权的方式有2种,

1.通过addFlags()来获取,仅使用setData,setDataandType以及setClipData方法传递uri时支持。

2.通过grantUrlPermission()方式,具体参考下文博客。

更多参考博客:

https://blog.csdn.net/lmj623565791/article/details/72859156

原文地址:https://www.cnblogs.com/fangg/p/10761568.html