美图欣赏 | 设为首页 | 加入收藏 | 网站地图

当前位置:电脑中国 > 编程 > 移动开发 >

Android N 7.0 应用间共享文件(FileProvider)

2018-03-02 14:58|来源:未知 |作者:dnzg |点击:

Android N 之前的 Uri

常规Uri有两种:

  • 媒体文件的Uri是content://, 表示这是一个数据库数据。去数据库查询正常返回。
  • 其他的文件Uri是file://, 表示这个是一个文件。这个uri是通过Uri.fromFile(File file)方法生成。

Android N 之前,这些uri可以传递到其他应用。

Android N 中共享文件

Android N 系统,Android 框架执行的 StrictMode,API 禁止向您的应用外公开 file://URI。 
如果一项包含文件 URI 的 Intent 离开您的应用,应用会停止运行,并出现 FileUriExposedException异常。官方文档在Android 7.0 行为变更进行了详细说明

android.os.FileUriExposedException: 
file:///storage/emulated/0/Download/appName-2.3.0.apk exposed beyond app through Intent.getData()
  • 1
  • 2

若要在应用间共享文件,您应发送一项 content://URI(代替file://URI),并授予 URI 临时访问权限。

FileProvider这个类就是把一个文件File,转换为 content://URI的

FileProvider是ContentProvider子类,所以FileProvider的使用方法,和ContentProvider使用基本上是一样的

如何共享文件,简单5步:

1、在AndroidManifest.xml中<application>标签下声明一个provider

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="app的包名.fileProvider"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

注意:  authorities:app的包名.fileProvider  grantUriPermissions:必须是true,表示授予 URI 临时访问权限 ( readPermission, writePermission, and permission attributes)  exported:true: The provider is available to other applications. false: The provider is not available to other applications.  resource:自定义的xml文件(下面会介绍)

2、在res目录下新建一个xml文件夹,并且新建一个file_paths的xml文件(如下图)

这里写图片描述

3、打开file_paths.xml文件,添加指定的分享目录:

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

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

每个节点都支持两个属性:

name:给这个访问路径起个名字  path:需要临时授权访问的相对路径(.代表所有路径)

path即为代表目录下的子目录,比如:

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

代表的目录即为:Environment.getExternalStorageDirectory()/pics,其他同理。

既然目的是使用content://uri替代file://uri,那么,content://的uri如何定义呢?总不能使用文件路径吧,那不是骗自己么~

所以,需要一个虚拟的路径对文件路径进行映射,所以需要编写个xml文件,通过path以及xml节点确定可访问的目录,通过name属性来映射真实的文件路径。

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

    <!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/DCIM/camerademo目录-->
    <external-path name="DCIM" path="DCIM/camerademo" />

    <!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/目录-->
    <external-path path="." name="external_storage_root" />

    <!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/Pictures/camerademo目录-->
    <external-path name="Pictures" path="Pictures/camerademo" />

    <!--代表app 私有的存储区域 Context.getFilesDir()目录下的images目录 /data/user/0/com.hm.camerademo/files/images-->
    <files-path name="private_files" path="images" />

    <!--代表app 私有的存储区域 Context.getCacheDir()目录下的images目录 /data/user/0/com.hm.camerademo/cache/images-->
    <cache-path name="private_cache" path="images" />

    <!--代表app 外部存储区域根目录下的文件 Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)目录下的Pictures目录-->
    <!--/storage/emulated/0/Android/data/com.xx.xxxxxx/files/Pictures-->
    <external-files-path name="external_files" path="Pictures" />

    <!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的images目录-->
    <!--/storage/emulated/0/Android/data/com.xx.xxxxxx/cache/images-->
    <external-cache-path name="external_cache" path="" />
</paths>

下面的例子使用SDCard,代码如下:

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

4、FileProvider API的使用

/**
     * 打开相机拍照
     *
     * @param activity
     * @return
     */
    public static void openCamera(Activity activity) {

        String filename = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA)
                    .format(new Date()) + ".png";

        File pictureFile = new File(Environment.getExternalStorageDirectory(), filename );

        Intent mIntent = new Intent();
        mIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

            Uri contentUri = FileProvider.getUriForFile(activity, "app的包名.fileProvider", pictureFile );
            //拍照结果输出到这个uri对应的file中
            mIntent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
            //对这个uri进行授权
            mIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            //拍照结果输出到这个uri对应的file中
            mIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(pictureFile ));
        }

        mIntent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
        activity.startActivityForResult(mIntent, REQUEST_CAMERA_IMAGE);
    }
					
(责任编辑:dnzg)
新锦江娱乐 关闭广告
新锦江娱乐 关闭广告