解惑Android

Android 7.0适配

2018-02-08  本文已影响9人  codeKeeper

目前在项目中遇到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会有一些风险,比如:

解决办法如下:

首先在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的优点:

定义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>

<paths>中可以定义以下子节点

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

file://到content://的转换规则:

代码中生成Content Uri

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

有两种设置文件的访问权限:

上一篇 下一篇

猜你喜欢

热点阅读