美文网首页
适配Android7.0应用间文件共享FileProvider

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

作者: 程序员小华 | 来源:发表于2018-12-26 14:22 被阅读0次

    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节点下支持以下几个子节点:

    • <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()
      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);
        }
    

    相关文章

      网友评论

          本文标题:适配Android7.0应用间文件共享FileProvider

          本文链接:https://www.haomeiwen.com/subject/pqdhlqtx.html