Android7.0之后请使用FileProvider

举报
yd_221104950 发表于 2020/12/04 00:54:55 2020/12/04
【摘要】 在官方7.0的以上的系统中,尝试传递 “file:/// Uri”可能会触发FileUriExposedException。FileProvider是 ContentProvider的子类。它的作用是通过创建“content:// Uri ”的方式来实现文件的安全共享。 我们可以授予临时访问权限(只读、只写或两者)给一个content URI。授予临时权限的最常见的方...

在官方7.0的以上的系统中,尝试传递 “file:/// Uri”可能会触发FileUriExposedException。FileProvider是 ContentProvider的子类。它的作用是通过创建“content:// Uri ”的方式来实现文件的安全共享。

我们可以授予临时访问权限(只读、只写或两者)给一个content URI。授予临时权限的最常见的方式是调用Intent.setFlags()。把这个Intent发送给其他app,让它们可以共享此文件。而授予的权限的有效性,只要接收Intent的Activity是active状态时,权限就会一直有效。如果这个Intent是发给一个Service,那么只要Service是运行状态权限就是有效的。

”file:/// Uri“这种方式访问文件是不安全的。首先这种方式访问文件亦需要权限,即也存在授予临时权限的过程,但是这种对访问的控制的方式必须修改文件系统底层文件的权限来实现。这种方式授予的临时权限会变得对任何一个app都可用的(“爱泛滥”),而且权限会一直有效,直到你改变它为止或者重启手机设备。在Android7.0之后,就通过FileProvider的方式,进一步约束我们的权限泛滥的范围,避免出现权限泄漏的问题。

使用FileProvider的步骤:

步骤1:在AndroidManifest.xml定义FileProvider

<manifest> ... <application> ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.fileprovider" android:exported="false" android:grantUriPermissions="true"> ... </provider> ... </application>
</manifest>

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

FileProvider已经提供了很多默认的功能,包括为文件生成content URI,所以我们没有必要再去重新定义子类,如果你想修改它的默认行为,可以继承FileProvider写个子类。

  • android:name 属性设置 为androidx.core.content.FileProvider或android.support.v4.content.FileProvider。类名必须是全路径的

  • android:authorities属性设置为一个类似域名的字符串。到时这个URI授权字符串,就是用来授权用的,它将成为content Uri的路径的一部分,就像网站的域名。

  • android:exported属性设置为false,因为它没有必要公开。

  • android:grantUriPermissions属性设置为true,这样才会允许你授予对文件的临时访问权限

步骤2:指定可用的文件
FileProvider只能为事先指定的目录里的文件生成content URI。 所以这一步我们就要指FileProvider可以为哪目录下的文件生成Content URI。这些目录可以通过在values/xml目录创建一份xml文件,然后在里面指定,如创建files_path.xml,在文件里加上相应的目录路径:

<paths xmlns:android="http://schemas.android.com/apk/res/android"> <!--Environment.getExternalStorageDirectory()--> <external-path name="files" path="aaa" />
</paths>

  
 
  • 1
  • 2
  • 3
  • 4

它的语法格式:

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

  
 
  • 1
  • outsideName的值是路径的一部分,它是用了隐藏真实的路径的。
  • actualPath才是真实的路径。

最后,要在AndroidManifest.xml文件的provider标签里用meta-data来指定:

<manifest> ... <application> ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> ... </application>
</manifest>

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

步骤3:牛刀小试,为一个文件生成Content URI
通过content URI与另一个app共享一份文件前,我们的app必须生成要共享文件的content URI。为这个共享文件生成一个新的File实例。然后把file实例传给getUriForFile(),它就会为你返回一个content URI了。
返回的content URI可以被放在一个Intent中发给另一个app。收到这个Intent的app就可以通过调用ContentResolver.openFileDescriptor获得一个ParcelFileDescriptor(包文件描述符)来打开文件并访问文件的内容。

File filePath = new File(Environment.getExternalStorageDirectory(), "aaa");
File file = new File(filePath, "index.html");
Uri uri = FileProvider.getUriForFile(getApplicationContext(),"com.mydomain.fileprovider",file);
//content://com.mydomain.fileprovider/files/index.html

  
 
  • 1
  • 2
  • 3
  • 4

步骤4:但是在发送Intent前,要先授予临时权限给content URI
授予content URI临时权限有以下几种方式:
(1)调用Context.grantUriPermission(package, Uri, mode_flags):
package:还指明临时权限授予的包。
Uri:包含content URI
mode_flags:可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION(只读权限), Intent.FLAG_GRANT_WRITE_URI_PERMISSION(只写权限)或者两者。这种方式授予的权限会一直起作用直到你撤销或重启手机设备为止才消失。
(2)通过调用setData()将content URI放在Intent中
(3)调用Intent.setFlags() :值可以填Intent.FLAG_GRANT_READ_URI_PERMISSION或Intent.FLAG_GRANT_WRITE_URI_PERMISSION或两者。

最后,就可以将这个Intent发给其他app,让它们共享这个文件了。

通过Intent授予的临时权限,只要接收Intent的Activity是活动(active状态)的,那么权限就会一直生效。当Activity所在的栈结束时,权限就会被自动移除。临时权限授予给某个app的Activity,这个权限会扩展到它所在的整个app,只有这个app栈结束时,权限才会被移除。

步骤5:提供Content URI给其他App
最常用的方式是调用startActivityResult(),这种方式就是发送一个Intent给你自己的app去打开另一个Activity。作为回应,我们的app会马上返回一个content URI给客户端app或者呈现一个允许我们选择文件用户界面。对于后者,我们一旦选择了某个文件,我们的app就会返回它的content URI。但两者两者最后都会通过setResult()的方式返回一个包含了content URI的Intent。

我们还可以调用Intent.setClipData()将content URI放到ClipData对象中,并将此对象添加到一个Intent中,再发送给其他app。使用这种方法,我们可以添加很多ClipData对象到Intent中,每一个对象都同它自己的content URI绑定。当我们使用Intent.setFlags()这种方式设置临时访问权限时,这些权限会应用到所有的content URI上。

Demo已上传GitHub,欢迎下载学习交流。

谢谢阅读

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/97409879

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。