适配Android N (7.0)需要解决的问题汇总
来源:互联网 发布:java 多线程 编辑:程序博客网 时间:2024/06/17 15:40
java.lang.SecurityException: MODE_WORLD_READABLE no longer supported异常的原因与解决
MODE_WORLD_READABLE文件权限存在安全隐患,所以谷歌先把它标为过时。然后完全的移除掉。
MODE_WORLD_READABLE在Android M之前都是能用的,但是在Android N,使用它会抛出****SecurityException,
所以我们可以换一个来使用,比如我用MODE_PRIVATE代替MODE_WORLD_READABLE。
// 在Android N之后,下面俩种情况会出SecurityExceptionFileOutputStream fos = context.openFileOutput(name,Context.MODE_WORLD_READABLE);SharedPreferences sp = context.getSharedPreferences("", Context.MODE_WORLD_READABLE)
以上俩段代码,在Android M以前,都是没问问题,但是的在Android N以及以后的版本 ,会报SecurityException异常,修改为下面方式即可
FileOutputStream fos = context.openFileOutput(name,Context.MODE_PRIVATE);SharedPreferences sp = context.getSharedPreferences("", Context.MODE_PRIVATE)
FileProvider去获取图片的Uri(FileUriExposedException异常的原因与解决)
在Andorid7.0之后,如果我们打开相机或者相册,获取其中图片的Uri,即通过file://URL开头的Url,就会报出FileUriExposedException。
7.0之前调用相机
在Android 7.0之前,打开相机拍照,获取照片的Uri,代码为
String imagePath= Environment.getExternalStorageDirectory()+"/demo/"Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);String fileName = "image.jpg";File file = new File(CACHE_IMG, fileName);Uri uri = Uri.fromFile(file); // 获取照片的Uriintent.putExtra(MediaStore.EXTRA_OUTPUT, uri);startActivityForResult(intent, 100);
上面的代码会在造成FileUriExposedException异常。因为通过Uri.fromFile(file)获取uri造成了file://URI暴漏。
7.0后怎样获取照片Uri
首先,我们需要在Application中定义一个内容提供者
<application ...> <provider android:name="android.support.v4.content.FileProvider" //固定值 android:authorities="com.dujun.demo.fileprovider" //路径 前面为包名,后面为fileprovider固定值,使用包名便于区分 android:exported="false" //是否支持其它应用调用当前组件 ,要求为flase android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" //固定值 android:resource="@xml/filepaths"/> //在res目录下xml文件夹下定义的filepaths.xml文件,名字可以自定义 </provider> </application>
接着,在res目录下定义xml文件夹,然后创建一个filepaths.xml的文件(名称与在清单文件中配置的resource中的名称要相同)
<paths> <external-path path="demo" name="my_demo" > </paths>
注意:XMl文件是唯一设置分享的目录,不能用代码去设置
filepaths.xml中标签的解释:
1. <files-path>
这个标签是/data/data//files目录 即getFilesDir()
获取到的目录。
2. <cache-path>
这个标签表示/data/data//cache目录 即getCacheDir()
获取到的目录。
3. <external-path>
表示 Environment.getExternalStorageDirectory()
获取到的目录,即
SDCard/Android/data/{package name}/files/目录。
4.<external-files-path>
Context#getExternalFilesDir(String) Context.getExternalFilesDir(null).
5.<external-cache-path>
Context.getExternalCacheDir().
在标签中,是通过键值对来设置属性,通常有name和path
path:代表当前标签所表示的目录的下一级目录 比如:<external-path path="demo/"
这个表示的目录为Environment.getExternalStorageDirectory()+"/demo/"
name:代表定义在Content中的字段 比如:name = “demoimage” ,并且请求的内容的文件名为demo_image.jpg 则 返回一个URI content://com.example.myapp.fileprovider/demoimage/demo_image.jpg
接这,开始使用。
继续上面调用相机代码
String imagePath= Environment.getExternalStorageDirectory()+"/demo/"Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);String fileName = "image.jpg";File file = new File(CACHE_IMG, fileName);Uri uriForFile = FileProvider.getUriForFile(context, "com.dujun.demo.fileprovider", file); // intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);startActivityForResult(intent, 100);
主要的不同就是在获取图片的Uri上面,在Andorid N之前 Uri uri = Uri.fromFile(file);
获取
在Android N之后 Uri uriForFile = FileProvider.getUriForFile(context, "com.dujun.demo.fileprovider", file);
获取。
4.4以下版本java.lang.SecurityException: Permission Denial异常的原因与解决。
但是,当我们使用以上方式运行在4.4的模拟器上时,应用还是crash.抛出了Permission Denial~异常。
因为在低版本的系统,我们的contentprovider的export设置的也是false;导致Permission Denial。
那么我们能否设置export=true.
查看FileProvider内部代码:
@Overridepublic void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); // Sanity check our security if (info.exported) { throw new SecurityException("Provider must not be exported"); } if (!info.grantUriPermissions) { throw new SecurityException("Provider must grant uri permissions"); } mStrategy = getPathStrategy(context, info.authority);}
发现,如果设置为true,会抛出throw new SecurityException("Provider must not be exported");
解决方式1:授权
context中有俩个方法:
**grantUriPermission(String toPackage, Uri uri,
int modeFlags)**
revokeUriPermission(Uri uri, int modeFlags)
其中grantUriPermission(String toPackage, Uri uri,
int modeFlags)就是授权方法
param toPackage
: 这个参数即包名,你给哪个应用授权,就传哪个应用的包名
所以这个包名需要动态的获取。
根据Intent查询出的所以符合的应用,都给他们授权
int flag = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSIONList<ResolveInfo> resInfoList = context.getPackageManager() .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; // 获取到要授权的应用的包名 context.grantUriPermission(packageName, uri, flag); // 调用授权代码进行授权}
在不需要的时候可以调用revokeUriPermission移除权限
解决方式2(推荐)
判断版本,这种方式最简单,也
Uri fileUri = null;if (Build.VERSION.SDK_INT >= 24) { fileUri = FileProvider.getUriForFile(context, "com.dujun.demo.fileprovider", file); } else { fileUri = Uri.fromFile(file);}
使用FileProvider安装Apk
在Android N 之前 我们安装Apk的代码为:
/** * @params file 需要安装的apk文件 */public static void installApk(File file) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); startActivity(intent);}
同样,在7.0 的系统上,还是抛出FileUriExposedException
修改代码
Uri fileUri = null;if (Build.VERSION.SDK_INT >= 24) { fileUri = FileProvider.getUriForFile(context, "com.dujun.demo.fileprovider", file); } else { fileUri = Uri.fromFile(file);}
继续,又抛出了SecurityException: Permission Denial的警告。没有crash
通过上面grantUriPermission解决这个问题,没有问题。
但是在这里,可以使用另外一种便捷的方式去解决警告
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
引入的第三方库中已经配置了FileProvider(Manifest merger failed问题的解决)
但我们在项目中使用了FileProvider,出现下面的错误:
Error:Execution failed for task ':app:processDebugManifest'.> Manifest merger failed : Attribute meta-data#android.support.FILE_PROVIDER_PATHS@resource value=(@xml/filepath) from AndroidManifest.xml:815:17-49 is also present at [com.jph.takephoto:takephoto_library:4.0.3] AndroidManifest.xml:24:17-51 value=(@xml/file_paths). Suggestion: add 'tools:replace="android:resource"' to <meta-data> element at AndroidManifest.xml:813:13-815:52 to override.
通过查看错误信息中的这句话
is also present at [com.jph.takephoto:takephoto_library:4.0.3] AndroidManifest.xml:24:17-51 value=(@xml/file_paths).
我们看到了出错的原因:因为我使用了第三方的com.jph.takephoto:takephoto_library
的库,在这个库中也配置了FileProvider.
继续看错误信息中给出的解决方案:
Suggestion: add 'tools:replace="android:resource"' to <meta-data> element at AndroidManifest.xml:813:13-815:52 to override.
重点内容
在我们的<meta-data
标签中加入这句tools:replace="android:resource"
然后在manifest节点中加入命名空间xmlns:tools="http://schemas.android.com/tools"
<application ...> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.dujun.demo.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data tools:replace="android:resource" <!--加入这句代码即可解决问题,需要在manifest节点加入命名空间--> android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/> </provider> </application>
总结
为什么我们在拍照的时候,4.4设备出现权限问题,不通过addFlag去解决。
因为addFlags主要用于setData,setDataAndType以及setClipData(注意:4.4时,并没有将ACTION_IMAGE_CAPTURE转为setClipData实现)这种方式
所以addFlags方式对于ACTION_IMAGE_CAPTURE在5.0以下是无效的,所以需要使用grantUriPermission,如果是正常的通过setData分享的uri,使用addFlags是没有问题的
- 适配Android N (7.0)需要解决的问题汇总
- N多android 解决不了的问题
- 需要解决的问题
- 需要解决的问题
- Android 小问题汇总解决
- Android模拟器上开发Phone.apk需要解决的问题
- 两个需要解决的问题
- JsmartWatch需要解决的问题
- 20061022需要解决的问题
- SOA 需要解决的问题
- codedom 需要解决的问题
- 需要解决的编程问题。
- 需要解决的问题(不断更新)
- 近期需要解决的问题
- 待解决的问题汇总
- N皇后问题的解决
- Shell脚本需要注意的问题汇总
- 晶振需要注意的问题汇总
- 创建swift颜色类
- js动态拼接标签,样式丢失的解决方法
- Java中System.currentTimeMills的几个常见用法
- sql查询重复数据
- 滑动监听,当上拉时上面标题栏逐渐显示出来
- 适配Android N (7.0)需要解决的问题汇总
- 安卓在布局中控件显示在最上层
- POJ
- swift 协议的使用方法和场景
- MIPI DSI协议介绍
- ios 生成字母加数字的随机数
- boost log库学习二(日志过滤功能)
- C Looooops poj 2142 扩展欧几里德算法
- 史上最全SSM框架整合(二)-----SSM框架实践搭建