7.0新特性-私有目录被限制访问

卓官方为了提高私有文件的安全性,面向 Android 7.0 或更高版本的应用私有目录被限制访问 (0700)。此设置可防止私有文件的元数据泄漏,如它们的大小或存在性.

传递软件包网域外的 file:// URI 可能给接收器留下无法访问的路径。因此,尝试传递 file:// URI 会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider

1.定义一个FileProvider(由于FileProvider是继承ContentProvider,属于四大组件之一,需要在AndroidManifest.xml中配置)

注意一点,android:authorities不要写死,因为该library最终可能会让多个项目引用,android:authorities是不可以重复的如果两个app中定义了相同的,则后者无法安装到手机中(authority conflict)。

<!-- fileProvider 用于兼容7.0 添加可用权限的文件目录-->
        <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/file_provider_paths"/>
        </provider>

2.添加可用权限的文件目录

在res资源目录下新建xml文件下,在该文件夹下创建file_provider_paths.xml文件,这个xml文件名并不是一定要这么起,只要和清单文件中配置的文件名一致就行

<?xml version="1.0" encoding="utf-8"?>
<!--新建xml文件夹, src\main\res\xml\file_provider_paths.xml-->
<resources>

    <paths>
        <!-- path="",是有特殊意义的,它代码根目录(下面同理),也就是说你可以向其它的应用共享根目录
            及其子目录下任何一个文件了,如果你将path设为path=”pictures”,
            那么它代表着根目录下的pictures目录(eg:/storage/emulated/0/pictures)
            即:Environment.getExternalStorageDirectory()目录或者其子目录。-->
        <external-path path="" name="myFile"></external-path>

        <!--表示Context.getFilesDir()目录或者其子目录。
            示例 : /data/data/com.chen.gradle/files/test1-->
        <files-path name="name1" path="test1" />

        <!--表示Context.getCacheDir()目录或者其子目录。
            示例 : /data/data/com.chen.gradle/cache/test2-->
        <cache-path name="name2" path="test2" />

        <!--表示Context.getExternalFilesDir(null)目录或者其子目录。
            示例 : /storage/emulated/0/Android/data/com.chen.gradle/files/test4-->
        <external-files-path name="name4" path="test4" />

        <!--表示Context.getExternalCacheDir()目录或者其子目录。
            示例 : /storage/emulated/0/Android/data/com.chen.gradle/cache/test5-->
        <external-cache-path name="name5" path="test5" />

    </paths>

</resources>

3.增加到provider

4.通过provider生成Uri

5.赋予临时权限给Uri

比如安装apk时:

public static void installApk(Context context, String apkPath) {
        if (context == null || TextUtils.isEmpty(apkPath)) {
            return;
        }
        File file = new File(apkPath);//将该路径增加到provider
        Intent intent = new Intent(Intent.ACTION_VIEW);
        //判读版本是否在7.0以上
        if (Build.VERSION.SDK_INT >= 24) {
            //通过provider生成Uri                               需要与在清单文件中注册的provider的 authorities一致
            Uri apkUri = FileProvider.getUriForFile(context, "工程包名.fileprovider", file);
            //赋予临时权限给Uri
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }

鸿洋的总结,工具类:http://blog.csdn.net/lmj623565791/article/details/72859156 

 

public class FileProvider7 {

    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;
    }

    private static Uri getUriForFile24(Context context, File file) {
        Uri fileUri = android.support.v4.content.FileProvider.getUriForFile(context,
                context.getPackageName() + ".myfileprovider",//切记需要与清单文件中的authorities一致
                file);
        return fileUri;
    }


    /**
     *
     * @param context
     * @param intent
     * @param type
     * @param file
     * @param writeAble 是否赋予写入权限,比如安装apk的时候就需要
     */
    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);
        }
    }

}

 切记:

 

 

但是此时我们项目中可能会用到其他一些第三方sdk有用到拍照功能的话,他也为了适配android7.0也添加了这个节点,其实很简单我们只要重写一个类 继承自FileProvider,然后就按上述方法在添加一个节点就可以了;例如;

<!--但是此时我们项目中可能会用到其他一些第三方sdk也配置了FileProvider节点,
            此时有些人可能就不知道如何下手了,其实很简单我们只要重写一个类 继承FileProvider,
            然后就按上述方法再添加一个节点就可以了;例如;-->
        <provider
            android:name=".service.UpdateFileProvider"
            android:authorities="${applicationId}.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/app_file_paths"/>
        </provider>

 所以:切记android:authorities="${applicationId}.fileProvider"

import android.support.v4.content.FileProvider;

/**
 * 解决需要添加多个FileProvider的问题
 */
public class UpdateFileProvider extends FileProvider {
    
}

 

posted @ 2017-06-27 12:03  ts-android  阅读(1768)  评论(0编辑  收藏  举报