日常夜拍
1 概述
由于前段时间做的应用都是车机上的(Android6.0),并不需要太多适配。想想到现在,Android已经到9了,貌似我好久都没做过适配了,这些适配的工作还是有必要做一些记录。
参考资料
通常开发中,适配Android7.0主要就是适配其中文件访问权限。比如调用系统拍照,应用更新后安装功能都是涉及到。
在官方7.0的以上的系统中,尝试传递 file://URI可能会触发FileUriExposedException
本文内容主要参考鸿洋_的文章,仅作记录。
2 使用FileProvider
2.1 声明provider,设置文件
在清单文件中声明provider标签,如:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="me.newtrekwang.android7.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /></provider>
android:authorities
表示授权者,这里的格式一般是[appId].fileprovider
android:exported
只能为falseandroid:grantUriPermissions="true"
表示授权Uri权限 ,且必须为true
meta-data
里设置指定的文件目录,为引用某个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>
<root-path/>
代表设备的根目录new File("/");<files-path/>
代表context.getFilesDir()<cache-path/>
代表context.getCacheDir()<external-path/>
代表Environment.getExternalStorageDirectory()<external-files-path>
代表context.getExternalFilesDirs()<external-cache-path>
代表getExternalCacheDirs()
比如我常用的:
<?xml version="1.0" encoding="utf-8"?><paths> <!--存储卡的Pictures目录放图片文件--> <external-path name="my_images" path="Pictures"/> <!--存储卡的flyaudiosmart2019/apk目录放安装包文件--> <external-path name="my_download" path="flyaudiosmart2019/apk"/></paths>
2.2 使用FileProvider API
调用系统拍照,构造Intent就需要传入一个Uri,那么Uri就必须使用FileProvider来获取,例如:
// 文件名 String filename = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA) .format(new Date()) + ".png"; // file对象,注意路径要和resource xml里配置的一样 File file = new File(Environment.getExternalStorageDirectory()+File.separator+"Pictures", filename); // 生成Uri Uri fileUri = FileProvider.getUriForFile(this, "me.newtrekwang.android7.fileprovider", file); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
其实Uri的实际内容为:content://me.newtrekwang.android7.fileprovider/my_images/Pictures/20190210-041411.png
,即格式为content://authorities/定义的name属性/文件的相对路径
但是这样统一用FileProvider获取Uri是不够的,在Android7可以正常运行,但是Android4.4就会报错
Caused by: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{52b029b8 1670:com.android.camera/u0a36} (pid=1670, uid=10036) that is not exported from uid 10052at android.os.Parcel.readException(Parcel.java:1465) at android.os.Parcel.readException(Parcel.java:1419) at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:2848) at android.app.ActivityThread.acquireProvider(ActivityThread.java:4399)
因为低版本的系统,仅仅是把这个当成一个普通的Provider在使用,而我们没有授权,contentprovider的export设置的也是false;导致Permission Denial。
所以唯一的办法就是授权了,可以使用Context的grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION| Intent.FLAG_GRANT_WRITE_URI_PERMISSION)!
,不过加了步授权,很麻烦。
最简单的方法是判断下版本,低版本直接获取Uri就行了。
即:
Uri fileUri = null;if (Build.VERSION.SDK_INT >= 24) { fileUri = FileProvider.getUriForFile(this, "me.newtrekwang.android7.fileprovider", file); } else { fileUri = Uri.fromFile(file); }
在拍照场景这样的Uri是不会出问题的,注意:在安装apk的业务场景下,intent用这样的Uri还是会报错,请求安装的intent,还要加上intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
,否则还是要步授权设置
3 总结:统一的方案:单独一个Module做FileProvder配置
1 创建一个library
的Module,然后在该清单文件定义你的FileProvider,然后在路径配置文件中定义项目中所有要用到的路径。
2 定义一个工具,功能就是获取合适的Uri
FileProviderUtils.java
/** * @author newtrekWang * @fileName FileProviderUtils * @createDate 2019/2/14 11:47 * @email 408030208@qq.com * @desc FileProvider工具类,用于适配Android7.0的文件访问变更 */public final class FileProviderUtils { /** * 获取合适的File Uri * @param context * @param file * @return */ public static Uri getUriForFile(Context context, File file) { Uri fileUri = null; if (Build.VERSION.SDK_INT >= 24) { fileUri = getUriForFile24(context, file); } else { fileUri = Uri.fromFile(file); } return fileUri; } /** * 获取File Uri from 安卓7及以上版本 * @param context * @param file * @return */ public static Uri getUriForFile24(Context context, File file) { Uri fileUri = android.support.v4.content.FileProvider.getUriForFile(context, "你自己的authorities", file); return fileUri; } /** * 用户安装apk场景 * @param context * @param intent * @param type * @param file * @param writeAble */ public static void setIntentDataAndType(Context context, Intent intent, String type, File file, boolean writeAble) { if (Build.VERSION.SDK_INT >= 24) { intent.setDataAndType(getUriForFile(context, file), type); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); if (writeAble) { intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } } else { intent.setDataAndType(Uri.fromFile(file), type); } }
作者:newtrek
链接:https://www.jianshu.com/p/bce6a4c779dd
共同学习,写下你的评论
评论加载中...
作者其他优质文章