您的当前位置:首页正文

Android 7.0 中FileProvider的适配

2024-11-08 来源:个人技术集锦

7.0系统上面出现了很多的变化;这里主要是记录FileProvider的变更影响

主要体现在两个地方:安装应用 和 相机拍照

1,应用安装

在7.0系统以前通常是通过action这样子进行安装的

Intent install = new Intent(Intent.ACTION_VIEW);
String pathString = intent.getStringExtra("downloadFile");
Uri uri  = Uri.fromFile(new File(pathString));;
install.setDataAndType(uri, "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
M_MainActivity.this.startActivity(install);

当运行在7.0及以上系统中是;会报错

Caused by: android.os.FileUriExposedException:
file:///storage/emulated/0/20170601-030254.png 
exposed beyond app through ClipData.Item.getUri()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1932)
at android.net.Uri.checkFileUriExposed(Uri.java:2348)

官方的解释是:

对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。

官方的解决方案:

要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。如需了解有关权限和共享文件的详细信息,请参阅共享文件。

 使用FileProvider 对 安装文件进行授权

<!--7.0系统 运行Uri.fromFile()修改-->
<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"?>
<resource xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="hm_file" path=".../files"/>
</resource>
Intent install = new Intent(Intent.ACTION_VIEW);
String pathString = intent.getStringExtra("downloadFile");
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
    uri = FileProvider.getUriForFile(M_MainActivity.this
            , getPackageName()+".fileprovider", new File(pathString));
}else
{
    uri = Uri.fromFile(new File(pathString));
}

install.setDataAndType(uri, "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
M_MainActivity.this.startActivity(install);

2.拍照;

同样,7.0以下的拍照通常是这样的

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {

    String filename = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA)
            .format(new Date()) + ".png";
    File file = new File(Environment.getExternalStorageDirectory(), filename);
    mCurrentPhotoPath = file.getAbsolutePath();

    Uri fileUri = FileProvider.getUriForFile(this, "com.zhy.android7.fileprovider", file);
    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
    startActivityForResult(takePictureIntent, REQUEST_CODE_TAKE_PHOTO);
}

适配7.0以及以上系统

Uri fileUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    fileUri = FileProvider.getUriForFile(this, "com.zhy.android7.fileprovider", file);
} else {
    fileUri = Uri.fromFile(file);
}

 

备注:

因为我们在7.0以上系统中exported必须是false;但是在以下系统中必须是true;否则在低版本上运行会报错

 Caused by: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{952c094 4768:com.google.android.packageinstaller/u0a18} (pid=4768, uid=10018) that is not exported from ui

上面都是通过红色部分来进行授权的;还有一种就是通过context提供的两个方法来授权

grantUriPermission(String toPackage, Uri uri,
int modeFlags)
revokeUriPermission(Uri uri, int modeFlags);

可以看到grantUriPermission需要传递一个包名,就是你给哪个应用授权,但是很多时候,比如分享,我们并不知道最终用户会选择哪个app,所以我们可以这样:

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

 

 

 

Top