Android 7.0适配

目前在项目中遇到Android 7.0及以上系统调用相机拍照时出现崩溃的情况,分析后发现是7.0系统的适配问题引起的,下面将收集到的7.0适配的相关资料整理以备忘。

Android 7.0对于文件共享权限做了进一步的限制,比如我们在调用系统相机拍照的时候经常会这样写:

void takePhoto(String cameraPhotoPath) {
    File cameraPhoto = new File(cameraPhotoPath);
    Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cameraPhoto));
    startActivityForResult(takePhotoIntent, REQUEST_TAKE_PHOTO);
}

这在一般情况下,运行没有问题,但是当把targetSdkVersion指定成24及之上并且在api>=24的设备上运行时,就会抛出异常:

android.os.FileUriExposedException:         
file:///storage/emulated/0/DCIM/IMG_20170125_144112.jpg exposed beyond app through ClipData.Item.getUri()
    at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
    at android.net.Uri.checkFileUriExposed(Uri.java:2346)
    at android.content.ClipData.prepareToLeaveProcess(ClipData.java:832)
    at android.content.Intent.prepareToLeaveProcess(Intent.java:8909)

7.0系统上Android不再允许app中把file://uri暴露给其他app,包括但不局限于通过Intent或ClipData等方法。原因在于使用file://Uri会有一些风险,比如:
- 文件是私有的,接收file://Uri的app无法访问该文件
- 在Android6.0之后引入运行时权限,如果接收file://Uri的app没有申请READ_EXTERNAL_STORAGE权限,在读取文件时会引发崩溃

解决办法如下:

首先在AndroidManifest.xml中添加provider

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths" />
</provider>

res/xml/provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

修改调用相机拍照的代码如下:

void takePhoto(String cameraPhotoPath) {
    File cameraPhoto = new File(cameraPhotoPath);
    Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    Uri photoUri;
    if (Build.VERSION.SDK_INT >= 24) {
        photoUri = FileProvider.getUriForFile(
                this,
                getPackageName() + ".provider",
                cameraPhoto);
    } else {
        photoUri = Uri.from(cameraPhoto);
    }
    takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
    startActivityForResult(takePhotoIntent, REQUEST_TAKE_PHOTO);
}
下面来具体讲讲这个FileProvider

使用content://Uri的优点:
- 它可以控制共享文件的读写权限,只要调用Intent.setFlags()就可以设置对方app对共享文件的访问权限,并且该权限在对方app退出后自动失效。相比之下,使用file://Uri时只能通过修改文件系统的权限来实现访问控制,这样的话访问控制对所有app都生效的,不能区分app
- 它可以隐藏共享文件的真实路径

定义FileProvider

FileProvider会隐藏共享文件的真实路径,将它转换成content://Uri路径,因此,我们还需要设定转换的规则。android:resource=”@xml/provider_paths”这个属性指定了规则所在的文件

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path  path="." name="root" />
    <files-path  path="images/" name="my_images" />
    <files-path  path="audios/" name="my_audios" />
    ...
</paths>

中可以定义以下子节点

子节点对应路径
files-pathContext.getFilesDir()
cache-pathContext.getCacheDir()
external-pathEnvironment.getExternalStorageDirectory()
external-files-pathContext.getExternalFilesDir(null)
external-cache-pathContext.getExternalCacheDir()

file://到content://的转换规则:
- 替换前缀:把file://替换成content://${android:authorities}
- 匹配和替换。遍历的子节点,找到最大能匹配上文件路径前缀的那个子节点,用path的值替换掉文件路径里所匹配的内容
- 文件路径剩余的部分保持不变

代码中生成Content Uri

File imagePath = new File(Context.getFilesDir(), "images");
File newFile = new File(imagePath, "2016/pic.png");
Uri contentUri = getUriForFile(getContext(), getPackageName() + ".fileprovider", newFile);

有两种设置文件的访问权限:
- 调用Context.grantUriPermission(package, uri, modeFlags)。这样设置的权限只有在手动调用Context.revokeUriPermission(uri, modeFlags)或系统重启后才会失效
- 调用Intent.setFlags()来设置权限。权限失效的时机:接收Intent的Activity所在的stack销毁时

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android是一个开源的移动操作系统,由Google开发。它的各个版本都有不同的特性和适配要求。下面是Android各个版本的适配情况: 1. Android 1.0:这是Android的首个正式版本,发布于2008年。它主要适配了当时的早期智能手机。 2. Android 1.5 Cupcake:这个版本引入了许多新特性,包括虚拟键盘、文本选择和复制功能等。它的适配要求相对较低,可以在较旧的设备上运行。 3. Android 1.6 Donut:这个版本增加了搜索框和快捷方式等功能。它的适配要求与Cupcake相似。 4. Android 2.0/2.1 Eclair:这个版本引入了许多新特性,包括支持多点触控、HTML5视频播放和Live壁纸等。它的适配要求相对较高,需要较新的设备支持。 5. Android 2.2 Froyo:这个版本引入了许多新特性,包括支持移动热点和Adobe Flash等。它的适配要求与Eclair相似。 6. Android 2.3 Gingerbread:这个版本引入了许多新特性,包括支持NFC和下载管理器等。它的适配要求相对较高,需要较新的设备支持。 7. Android 4.0 Ice Cream Sandwich:这个版本引入了许多新特性,包括全新的用户界面和面部解锁等。它的适配要求相对较高,需要较新的设备支持。 8. Android 4.1/4.2/4.3 Jelly Bean:这个版本引入了许多新特性,包括Google Now和通知增强等。它的适配要求与Ice Cream Sandwich相似。 9. Android 4.4 KitKat:这个版本引入了许多新特性,包括透明状态栏和打印支持等。它的适配要求相对较高,需要较新的设备支持。 10. Android 5.0/5.1 Lollipop:这个版本引入了许多新特性,包括Material Design和多用户支持等。它的适配要求相对较高,需要较新的设备支持。 11. Android 6.0 Marshmallow:这个版本引入了许多新特性,包括指纹识别和运行时权限等。它的适配要求相对较高,需要较新的设备支持。 12. Android 7.0/7.1 Nougat:这个版本引入了许多新特性,包括分屏模式和通知增强等。它的适配要求相对较高,需要较新的设备支持。 13. Android 8.0/8.1 Oreo:这个版本引入了许多新特性,包括自适应图标和通知渠道等。它的适配要求相对较高,需要较新的设备支持。 14. Android 9 Pie:这个版本引入了许多新特性,包括手势导航和应用程序切片等。它的适配要求相对较高,需要较新的设备支持。 15. Android 10:这个版本引入了许多新特性,包括暗黑模式和系统级录屏等。它的适配要求相对较高,需要较新的设备支持。 16. Android 11:这个版本引入了许多新特性,包括聊天气泡和无线Android Auto等。它的适配要求相对较高,需要较新的设备支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值