适配Android7.0应用间文件共享FileProvider

android编译版本升级到7.0以后,会出现很多适配方面的工作,从android官方文档对于android7.0行为变更可以了解到,android7.0的应用禁止传递类似file:// URI这样的链接,否则应用会抛出FileUriExposedException异常,比较典型的场景就是我们项目中调用摄像头拍照,如果不对这个进行适配,我们按照以前的代码调用摄像头拍照的时候,会出现以下错误:

android.os.FileUriExposedException: file:///storage/emulated/0/photoTest/photo.jpeg exposed beyond app through ClipData.Item.getUri()

接下来开始对这个进行适配,适配的方案主要参考了 鸿洋大神 Android 7.0 行为变更 通过FileProvider在应用间共享文件吧 这篇博客

1.首先在项目res目录下新建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="external_file_path"
        path="" />
    <external-cache-path
        name="external_cache_path"
        path="" />

</paths>

在paths节点下支持以下几个子节点:

  • :代表设备的根目录new File("/")
  • : 代表context.getFilesDir()
  • : 代表context.getCacheDir()
  • : 代表Environment.getExternalStorageDirectory()
  • : 代表context.getExternalFilesDirs()
  • : 代表getExternalCacheDirs()
    path节点支持name和path两个属性,配置了path属性就相当于在相应路径下子目录,例如:
<external-path
        name="external"
        path="phototest" />

这样配置就代表应用可以使用Environment.getExternalStorageDirectory()/phototest 目录以及其子目录的文件

2. 在AndroidManifest.xml的application节点下增加FileProvider的声明
<application>
        ...
        ...

        <!--适配android 7.0文件访问
              com.hua.phototest是应用的包名
        -->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.hua.phototest.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
    </application>
3.FileProvider工具类,参考自鸿洋大神博客
public class FileProvider7 {

    public static Uri getUriForFile(Context context, File file) {
        Uri fileUri = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            fileUri = getUriForFile24(context, file);
        } else {
            fileUri = Uri.fromFile(file);
        }
        return fileUri;
    }



    private static Uri getUriForFile24(Context context, File file) {
        Uri fileUri = android.support.v4.content.FileProvider.getUriForFile(context,
                context.getPackageName() + ".fileprovider",
                file);
        return fileUri;
    }


    public static void setIntentDataAndType(Context context,
                                            Intent intent,
                                            String type,
                                            File file,
                                            boolean writeAble) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            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);
            chmod("777", file.getAbsolutePath());//apk放在cache文件中,需要获取读写权限
        }
    }

    public static   void chmod(String permission, String path) {
        try {
            String command = "chmod " + permission + " " + path;
            Runtime runtime = Runtime.getRuntime();
            runtime.exec(command);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void setIntentData(Context context,
                                     Intent intent,
                                     File file,
                                     boolean writeAble) {
        if (Build.VERSION.SDK_INT >= 24) {
            intent.setData(getUriForFile(context, file));
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            if (writeAble) {
                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            }
        } else {
            intent.setData(Uri.fromFile(file));
        }
    }


    public static void grantPermissions(Context context, Intent intent, Uri uri, boolean writeAble) {

        int flag = Intent.FLAG_GRANT_READ_URI_PERMISSION;
        if (writeAble) {
            flag |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
        }
        intent.addFlags(flag);
        List<ResolveInfo> resInfoList = context.getPackageManager()
                .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            context.grantUriPermission(packageName, uri, flag);
        }
    }
}
4.最后在传递URI的时候调用相应的方法获取URI即可,例如下面代码是调用摄像头拍照:
private String mTempPhotoPath;
    private Uri imageUri;

    private void takePhoto() {
        Intent intentToTakePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File fileDir = new File(Environment.getExternalStorageDirectory() + File.separator + "photoTest" + File.separator);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }

        File photoFile = new File(fileDir, "photo.jpeg");
        mTempPhotoPath = photoFile.getAbsolutePath();
        //适配android7.0应用间文件共享
        imageUri = FileProvider7.getUriForFile(this, photoFile);
        intentToTakePhoto.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intentToTakePhoto, RC_TAKE_PHOTO);
    }

猜你喜欢

转载自blog.csdn.net/weixin_44247225/article/details/85261476