Android7.0+权限适配
2018-03-31 00:35
211 查看
谷歌在Android7.0(API 24)做了一些权限的更改,对用户私有目录或私有文件的访问和共享做了限制,具体可看官网描述权限更改这里简单解释一下,就是涉及传递file://URI在7.0+上就会抛FileUriExposedException异常。这样说有些刚入门的小伙伴或许还不是一脸懵逼,下面举两个常见的开发场景来解释一下传递file://URI具体什么操作(下面例子不涉及具体实现,只贴出关键代码做解释)。
//设置拍照后保存目录
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);上面代码最终会转为file:///你指定的目录/指定的照片名,在7.0以下运行正常,7.0及以上会抛出上面所说的FileUriExposedException
几点说明:
1.其中下面这一行中的com.example.myapplication必须改为自己项目的包名,包名在Module下build.gradle下android:authorities="com.example.myapplication.fileprovider"
![](https://img-blog.csdn.net/20180330161203887?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xqYjc4MDgzMDk5Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
2.倒数第二行涉及一个xml文件声明,这个在下一个点介绍android:resource="@xml/file_paths"
![](https://img-blog.csdn.net/20180330161730902?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xqYjc4MDgzMDk5Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![](https://img-blog.csdn.net/20180330161831891?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xqYjc4MDgzMDk5Nw==/font/5a6L5L2T/fontsize/400/f<br/>4000<br/>ill/I0JBQkFCMA==/dissolve/70)
然后打开xml文件<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="" path=""/>
<cache-path name="" path="" />
<external-path name="" path="" />
<external-files-path name="" path="" />
<external-cache-path name="" path="" />
<external-media-path name="" path="" />
</paths>标签说明
<paths>为顶层标签,它下面可添加任意个(0,1或多个)上面出现的标签
<paths>子标签解释
可以看到上面每个标签都含有一个name和path属性
name:这个可以随意写(用于隐藏真实的目录,后面再演示)
path:要共享的目录,只能是目录,不能是某个具体的文件
<files-path> 代表Context.getFilesDir()所指向的目录
<cache-path>代表Context.getCacheDir()所指向的目录
<external-path>代表Environment.getExternalStorageDirectory()所指向的目录
<external-files-path>代表Context.getExternalFilesDir()所指向的目录
<external-cache-path>代表Context.getExternalCacheDir()所指向的目录
<external-media-path>代表Context.getExternalMediaDirs()所指向的目录(API21+设备才能使用,所以一般不用)
以上解释可能有的小伙伴还是一脸懵逼,下面举个具体的下载例子说一下(不涉及代码)
比如我现在把新版本安装包下载在SD卡根目录下的Download文件夹下,那么我的xml如下设计即可
其中path就得指定为Download,name的值可随意写
![](https://img-blog.csdn.net/20180330163531905?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xqYjc4MDgzMDk5Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
关于name和path最终去向这里盗用博客里面一张图(如涉及侵权麻烦博主找我撤回)
由下图可知最终我们将原本的file://URI转为了content://URI,这个正是安卓7.0要求的正确传递方式,而我们上面的name最终传递到了URI的后面作为一个隐藏的目录,隐藏了原文件的真实目录
![](https://img-blog.csdn.net/20180330163650829?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xqYjc4MDgzMDk5Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
(1)不管是拍照还是应用更新,Android7.0及以上都使用FileProvider提供的API获取到最终的uri,这里要特别强调,也是上面第二个参数必须和清单文件设置的完全一样,即应用包名+fileprovider,也即如下红框中字符串
![](https://img-blog.csdn.net/20180330235147734?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xqYjc4MDgzMDk5Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
(2)眼尖的小伙伴肯定注意到应用更新中比拍照多了一行授权的代码。这是因为FileProvider内部进行了一个授权的判断,如果未授权,则会抛出异常,所以才需要加上那一行授权代码的。具体看下图代码和注释
![](https://img-blog.csdn.net/20180330235620267?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xqYjc4MDgzMDk5Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
到这里有的小伙伴又纳闷了,那都抛异常了,怎么拍照不也跟随潮流设置一波呢?那是因为拍照在创建Intent时传入的Action为ACTION_IMAGE_CAPTURE。添加这个值之后startActivity的时候会调用到Intent的一个方法migrateExtraStreamToClipData()方法,方法最后有段代码是进行判断Action是否为我们设置的这个值,如果是,会自动为我们添加上面的权限,所以我们才不用再次手动添加权限,具体如下图所示。由于安装apk我们只用到读取的权限即可,所以上面就没多添加写的权限
![](https://img-blog.csdn.net/20180331000552868?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xqYjc4MDgzMDk5Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
最后小伙伴就可以愉快地适配Android7.0关于file://URI的异常啦,不知道我举的两个例子能否让你能够举一反三呢?如发现内容有误,请指正,在此小弟先谢过了。如果不懂可留言,看到我会及时回复。
然后贴出官网的适配(原滋原味来一波) 适配步骤
关于适配还有更加牛逼的操作,本篇博客也有部分是摘抄自鸿洋大神的博客,鸿洋大神博客最后有个完美适配的解决方案,感兴趣的小伙伴可以自行查看,现在贴出链接 鸿洋大神完美适配
最后按照国际惯例(其实是自己的喜好),这里博主推荐大家俩款可谓牛逼轰轰滴神器。
正所谓工欲善其事必先利其器,天下武功唯快不破。平时阻碍我们开发进度的往往不是我们滴智商+情商(关情商鸟事。。。),而往往是永无至今龟速滴编译,有没有办法让编译速度加快到秒级呢?那是肯定的,请拿好以下两款神器。
JRebel for Android(秒级神器,唯一缺陷是得破解,要不然就翻墙提供一个FaceBook账号,让人家跟踪你的使用即可免费)
破解正确打开方式(失效勿喷)
JRebel官网(无需翻墙)
FindLine(阿里巴巴秒级神器,号称最快编译神器(比JRebel for Android还快,简直不是人。。。),具体多快传闻只有亲身体验滴人才能知晓。。。)
FindLine GitHub
拍照
当实现拍照需要设置拍照后照片存放目录,一般会涉及如下代码Uri uri = Uri.fromFile(new File("指定你要保存的目录"));//设置拍照后保存目录
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);上面代码最终会转为file:///你指定的目录/指定的照片名,在7.0以下运行正常,7.0及以上会抛出上面所说的FileUriExposedException
应用更新
应用更新下载完安装包启动系统安装界面涉及下面一句代码intent.setDataAndType(Uri.fromFile(new File("安装包路径")), "application/vnd.android.package-archive");这里跟上面拍照例子也用到了Uri.fromFile();这个api,同样在7.0下运行正常,7.0及以上依旧抛FileUriExposedException异常小结
凡是涉及Uri.fromUri(),又跟Intent相关的,如在安卓7.0及以上不做适配,就会抛异常。下面具体讲解如何适配声明Provider
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.myapplication.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>将以上复制粘贴到清单文件<application>节点下再修改下即可
几点说明:
1.其中下面这一行中的com.example.myapplication必须改为自己项目的包名,包名在Module下build.gradle下android:authorities="com.example.myapplication.fileprovider"
2.倒数第二行涉及一个xml文件声明,这个在下一个点介绍android:resource="@xml/file_paths"
编写xml
Android Studio可以鼠标点一个上面那个@xml/file_paths,然后按快捷键Alt+Enter,就会提示创建文件夹和文件,按回车,再点击OK即可自动在项目res目录下新建一个xml目录和一个名为file_paths的xml文件(不会快捷键的按照目录结构逐个创建即可)然后打开xml文件<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="" path=""/>
<cache-path name="" path="" />
<external-path name="" path="" />
<external-files-path name="" path="" />
<external-cache-path name="" path="" />
<external-media-path name="" path="" />
</paths>标签说明
<paths>为顶层标签,它下面可添加任意个(0,1或多个)上面出现的标签
<paths>子标签解释
可以看到上面每个标签都含有一个name和path属性
name:这个可以随意写(用于隐藏真实的目录,后面再演示)
path:要共享的目录,只能是目录,不能是某个具体的文件
<files-path> 代表Context.getFilesDir()所指向的目录
<cache-path>代表Context.getCacheDir()所指向的目录
<external-path>代表Environment.getExternalStorageDirectory()所指向的目录
<external-files-path>代表Context.getExternalFilesDir()所指向的目录
<external-cache-path>代表Context.getExternalCacheDir()所指向的目录
<external-media-path>代表Context.getExternalMediaDirs()所指向的目录(API21+设备才能使用,所以一般不用)
以上解释可能有的小伙伴还是一脸懵逼,下面举个具体的下载例子说一下(不涉及代码)
比如我现在把新版本安装包下载在SD卡根目录下的Download文件夹下,那么我的xml如下设计即可
其中path就得指定为Download,name的值可随意写
关于name和path最终去向这里盗用博客里面一张图(如涉及侵权麻烦博主找我撤回)
由下图可知最终我们将原本的file://URI转为了content://URI,这个正是安卓7.0要求的正确传递方式,而我们上面的name最终传递到了URI的后面作为一个隐藏的目录,隐藏了原文件的真实目录
使用
上面说了这么多,都只是热身,单纯配置而已,还没涉及一行代码,接下来就得开始用代码实现我们的功能了,这里也主要针对上面提到的两个例子(其它例子看完可举一反三,下面只展示修改部分,不涉及全部代码)这里得用到一个FileProvider类,它继承自ContentProvider,这也是为什么我们第一步得在清单文件注册provider的原因。拍照
//创建图片存放file File imgFile = new File("照片存放目录"); Uri uri; //根据当前系统版本决定使用哪个api,N是Android7.0的代号 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //第一个参数是上下文,第二个参数来自清单文件,必须完全一样,第三个参数为上面创建的照片file uri = FileProvider.getUriForFile(this, "com.example.myapplication.fileprovider", imgFile); } else { //Android7.0还用原先的api uri = Uri.fromFile(imgFile); } //设置拍照后保存目录 intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
应用更新
//创建安装包file File apkFile = new File("安装包路径"); Uri uri; //根据当前系统版本决定使用哪个api,N是Android7.0的代号 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //第一个参数是上下文,第二个参数来自清单文件,必须完全一样,第三个参数为上面创建的安装包file uri = FileProvider.getUriForFile(this, "com.example.myapplication.fileprovider", apkFile); } else { //Android7.0还用原先的api uri = Uri.fromFile(apkFile); } //授权Intent读取URI和写URI的权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //设置拍照后保存目录 intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);几点说明
(1)不管是拍照还是应用更新,Android7.0及以上都使用FileProvider提供的API获取到最终的uri,这里要特别强调,也是上面第二个参数必须和清单文件设置的完全一样,即应用包名+fileprovider,也即如下红框中字符串
(2)眼尖的小伙伴肯定注意到应用更新中比拍照多了一行授权的代码。这是因为FileProvider内部进行了一个授权的判断,如果未授权,则会抛出异常,所以才需要加上那一行授权代码的。具体看下图代码和注释
到这里有的小伙伴又纳闷了,那都抛异常了,怎么拍照不也跟随潮流设置一波呢?那是因为拍照在创建Intent时传入的Action为ACTION_IMAGE_CAPTURE。添加这个值之后startActivity的时候会调用到Intent的一个方法migrateExtraStreamToClipData()方法,方法最后有段代码是进行判断Action是否为我们设置的这个值,如果是,会自动为我们添加上面的权限,所以我们才不用再次手动添加权限,具体如下图所示。由于安装apk我们只用到读取的权限即可,所以上面就没多添加写的权限
最后小伙伴就可以愉快地适配Android7.0关于file://URI的异常啦,不知道我举的两个例子能否让你能够举一反三呢?如发现内容有误,请指正,在此小弟先谢过了。如果不懂可留言,看到我会及时回复。
然后贴出官网的适配(原滋原味来一波) 适配步骤
关于适配还有更加牛逼的操作,本篇博客也有部分是摘抄自鸿洋大神的博客,鸿洋大神博客最后有个完美适配的解决方案,感兴趣的小伙伴可以自行查看,现在贴出链接 鸿洋大神完美适配
最后按照国际惯例(其实是自己的喜好),这里博主推荐大家俩款可谓牛逼轰轰滴神器。
正所谓工欲善其事必先利其器,天下武功唯快不破。平时阻碍我们开发进度的往往不是我们滴智商+情商(关情商鸟事。。。),而往往是永无至今龟速滴编译,有没有办法让编译速度加快到秒级呢?那是肯定的,请拿好以下两款神器。
JRebel for Android(秒级神器,唯一缺陷是得破解,要不然就翻墙提供一个FaceBook账号,让人家跟踪你的使用即可免费)
破解正确打开方式(失效勿喷)
JRebel官网(无需翻墙)
FindLine(阿里巴巴秒级神器,号称最快编译神器(比JRebel for Android还快,简直不是人。。。),具体多快传闻只有亲身体验滴人才能知晓。。。)
FindLine GitHub
相关文章推荐
- Android 应用适配 Android 7.0 权限要求详解
- Android 一行代码搞定将错误日志放入到sd卡中且不需要任何权限,适配到android7.0
- Android 7.0相机适配权限管理
- Android 版本更新(非热更新) 适配7.0更新 以及三星 note系列读取内存相关目录无权限问题
- Android 一行代码搞定将错误日志放入到sd卡中且不需要任何权限,适配到android7.0
- Android调用系统相机、相册功能,适配6.0权限获取以及7.0以后获取URI(兼容多版本)
- Android 应用适配 Android 7.0 权限要求
- 关于android 7.0 适配问题
- 视频框架 Vitamio 使用教程+部分心得 (六) 适配 Android 7.0 MediaController显示
- Android 权限及7.0权限的兼容
- Android笔记--Android6.0、7.0权限处理
- Android 6.0 权限使用 以及小米手机权限的适配
- android Bugly升级SDK适配Android 7.0
- Android - Android 7.0 拍照,相册选择和系统图片裁剪和删除适配
- 极其方便的系统原生图片选取库,已适配Android 7.0
- Android6权限适配-Easypermissions
- Android 7.0拍照权限处理
- Android 6.0运行时权限和7.0拍照(相册)并剪裁显示
- 关于 Android 7.0 适配中 FileProvider 部分的总结
- Android调用系统相机、图库、裁剪图片并压缩上传(适配7.0)