android共享文件夹_安卓多用户共享文件

android共享文件夹_安卓多用户共享文件AndroidN之前的Uri常规Uri有两种:媒体文件的Uri是content://,表示这是一个数据库数据。去数据库查询正常返回。其他的文件Uri是file://,表示这个是一个文件。这个uri是通过Uri.fromFile(Filefile)方法生成。AndroidN之前,这些uri可以传递到其他应用。AndroidN中共享文件Android

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

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()

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

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

FileProviderContentProvider子类,所以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)
exportedtrue: 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文件,添加指定的分享目录:

file_paths.xml 是这样的

<?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="name" path="path" />
     <external-cache-path name="name" path="path" />
</paths>

在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:需要临时授权访问的相对路径(.代表所有路径)

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

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

下面举一些例子:

<?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);
            //对这个uri进行授权
            mIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_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);
    }

核心代码就这一行了~

Uri contentUri = FileProvider.getUriForFile(activity, "app的包名.fileProvider", pictureFile );
  • 第二个参数就是我们在androidManife.xml 中的provider的参数authorities

  • 第三个参数是指定的文件File

生成的uri:

content://com.xuexuan.fileprovider/external/20171201-094017.png

可以看到格式为:content://authorities/paths中name的值/文件的相对路径,即name隐藏了可存储的文件夹路径。

这里需要多说一点,Uri 的最终路径,与file和path有很大关系

如果file的路径完全包含path路径的,则显示path路径+文件的相对路径,如下:

path路径 <external-path name="my_external" path="customscamera" />

file路径 /storage/emulated/0/customscamera/1534305129374.jpg

则 Uri 为:content://com.xuexuan.fileprovider/my_external/customscamera/1534305129374.jpg

如果file的路径不完全包含path路径的,则显示文件的绝对路径,如下:

path路径 <external-path name="my_external" path="123" />

file路径 /storage/emulated/0/customscamera/1534305129374.jpg

则 Uri 为:content://com.futureway.blealarm.fileProvider/profile_photo/storage/emulated/0/customscamera/1534305129374.jpg

5、对URI进行授权

第4步的代码,有一行注释是:对这个uri进行授权。

授权有两种方式:

  1. Intent.addFlags,该方式主要用于针对intent.setData,setDataAndType以及setClipData相关方式传递uri的。
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

2、使用函数进行授权和移除权限

  • grantUriPermission(String toPackage, Uri uri,
    int modeFlags)函数来进行授权

  • revokeUriPermission(Uri uri, int modeFlags);移除权限

方式二较为麻烦,因为需要指定目标应用包名,很多时候并不清楚,所以需要通过PackageManager进行查找到所有匹配的应用,全部进行授权。不过更为稳妥~

List<ResolveInfo> resInfoList = context.getPackageManager()
            .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    context.grantUriPermission(packageName, uri, flag);
}

如果没有授权,可能会遇到这样的错误

java.lang.SecurityException: Permission Denial: 
opening provider android.support.v4.content.FileProvider 
        from ProcessRecord{18570a 27107:com.google.android.packageinstaller/u0a26} (pid=27107, uid=10026) that is not exported from UID 10004

以下这两个问题,可参考这篇文章

  • 为什么在Android 7 设备上,Intent的action为ACTION_IMAGE_CAPTURE,不进行授权,不会遇到Permission Denial的问题

  • 为什么Android 4.4设备遇到权限问题,不通过addFlags这种方式解决

错误分析

报错

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

分析

在生成Uri 的时候,指定的文件所在的路径没有包含在path所指定的路径中

疑惑

遇到这样一个到现在没有理解的问题,在小米6,MIUI 9.0 上面,使用下面的代码,进行图片的裁剪。

1、如果不使用下面的函数授权,就会出现java.lang.SecurityException: Permission Denial的错误

2、使用了下面的函数授权,可以正常返回。但是返回的数据是null。但是在指定路径有裁剪后的照片输出。

有大神知道问题的原因,帮忙在评论里指导我一下,十分感谢

     /*** * 裁剪图片 * @param activity Activity * @param uri 图片的Uri */
    public static void cropPicture(Activity activity, Uri uri) { 
   
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.putExtra("crop", "true");// 才能出剪辑的小方框,不然没有剪辑功能,只能选取图片
        intent.putExtra("aspectX", 1); // 放大缩小比例的X
        intent.putExtra("aspectY", 1);// 放大缩小比例的X 这里的比例为: 1:1
        intent.putExtra("outputX", 120);  //这个是限制输出图片大小
        intent.putExtra("outputY", 120);
        intent.putExtra("return-data", false);
        //切图大小不足输出,无黑框
        intent.putExtra("scale", true);
        intent.putExtra("scaleUpIfNeeded", true);
        Log.e("FaceUtil", "图片path:" + uri.toString());

        File imageFile = new File(uri.getPath());

        //输出图片的路径
        File outputImageFile = new File(Environment.getExternalStorageDirectory(),
                "picture" + System.currentTimeMillis() / 1000 + ".jpg");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 
   
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

            Uri contentUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".fileProvider", imageFile);
            Uri outputUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".fileProvider", outputImageFile);

            intent.setDataAndType(contentUri, "image/*");

			//使用函数授权,所有的包名
            List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
            for (ResolveInfo resolveInfo : resInfoList) { 
   
                String packageName = resolveInfo.activityInfo.packageName;
                activity.grantUriPermission(packageName, outputUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }

            //裁剪后的图片,将被保存在这个uri中
            intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
        } else { 
   
            //裁剪后的图片,将被保存在这个uri中
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputImageFile));
            intent.setDataAndType(uri, "image/*");
        }

        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        activity.startActivityForResult(intent, REQUEST_CROP_IMAGE);
    }

请点赞、收藏,感谢大家的支持,有任何疑问可在评论区回复
参考:

Android 7.0 行为变更 通过FileProvider在应用间共享文件吧

https://developer.android.com/guide/topics/manifest/provider-element.html

Android7.0须知–应用间共享文件(FileProvider)

解决 Android N 7.0 上 报错:android.os.FileUriExposedException

FileProvider无法获取外置SD卡问题解决方案 | Failed to find configured root that contains

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

Android中Uri和Path之间的转换

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/196711.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 编码器计数原理与电机测速原理——多图解析[通俗易懂]

    编码器计数原理与电机测速原理——多图解析[通俗易懂]编码器,是一种用来测量机械旋转或位移的传感器。它能够测量机械部件在旋转或直线运动时的位移位置或速度等信息,并将其转换成一系列电信号。编码器分类按监测原理分类光电编码器光电编码器,是一种通过光电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器。这是目前应用最多的传感器,光电编码器是由光源、光码盘和光敏元件组成。光栅盘是在一定直径的圆板上等分地开通若干个长方形孔。由于光电码盘与电动机同轴,电动机旋转时,光栅盘与电动机同速旋转,经发光二极管等电子元件组成的检测装置检测输出若干脉冲信号,通过计算每

    2022年10月1日
    2
  • 012路规律怎么看_双元素集合怎么判断

    012路规律怎么看_双元素集合怎么判断堆题目链接将一系列给定数字顺序插入一个初始为空的小顶堆H[]。随后判断一系列相关命题是否为真。命题分下列几种:x is the root:x是根结点;x and y are siblings:x和y是兄弟结点;x is the parent of y:x是y的父结点;x is a child of y:x是y的一个子结点。输入格式:每组测试第1行包含2个正整数N(≤ 1000)和M(≤ 20),分别是插入元素的个数、以及需要判断的命题数。下一行给出区间[−10000,10000]内的N个要被

    2022年8月9日
    9
  • python从linux下载文件_python gzip

    python从linux下载文件_python gzip解决python调用OpenCV保存视频时使用”avc1″格式出现#Couldnotfindencoderforcodecid27:Encodernotfound的错误(此错误不能保存视频文件),以及使用”mpeg”格式出现的#OpenCV:FFMPEG:tag0x6765706d/’mpeg’isnotsupportedwithcodecid2a…

    2022年9月25日
    3
  • iscsiadm命令详解_iscsi 局域网

    iscsiadm命令详解_iscsi 局域网启动iscsi守护进程serviceiscsistart发现目标iscsiadm-mdiscovery-tsendtargets-p192.168.1.1:3260-mdiscovery指定模式为discovery-p192.168.1.1:3260指定目标ip和端口登入节点iscsiadm-mnode–Tiqn.19…

    2022年8月23日
    8
  • 罗永浩一个坑位卖60万脏钱背后:放下面子赚钱,才是成年人最大的体面

    罗永浩一个坑位卖60万脏钱背后:放下面子赚钱,才是成年人最大的体面作者l粥左罗来源l粥左罗(ID:fangdushe520)转载请联系授权(微信ID:zzlloveutoo)01罗永浩终于向钱低头了做主播赚钱丢人么?多少钱能让罗永浩低头?抖…

    2022年9月21日
    3
  • Qt Creator 安装 VLD

    Qt Creator 安装 VLD一 环境说明 1 VLD nbsp 内存检测工具 只能检测使用 VC 编译器 不能用于检测 MinGW 编译器 nbsp nbsp 所以要检测 nbsp Qt 内存泄露问题编译器一定要是 MSVC 环境要求 nbsp 1 VLD nbsp 版本要 2 X 以上 nbsp 不能使用 1 X 的版本 否则检测不准确 Qt 检测会提示很多内存泄露 本人使用 vld 2 3 setup exe nbsp 2 VC 编译器 nbsp 即 MSVC nbsp 如果有安装 VS 则就有这编译器 nbsp

    2025年12月6日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号