xml地图|网站地图|网站标签 [设为首页] [加入收藏]

永利皇宫463com:Android 7.0权限适配:FileUriExposedException异常

有的是开采者应该都清楚android7.0以上安装apk的主题素材。但是看了生龙活虎部分篇章,并不曾很好的扶助作者消除难点。何况还也许有的坑未有被波及。

永利皇宫463com,前言

在Android7.0系统上,android框架强迫推行了 StrictMode API 政策防止向你的行使曾外祖父开 file:// U奥迪Q5I。 借使大器晚成项包蕴文件 file:// U大切诺基I类型 的 Intent 离开你的使用,应用失利,并冒出 FileUriExposedException 非常,如调用系统相机拍录,或裁切照片。

后天来聊聊Android 7.0 FileUriExposedException至极,以至它的选用方法和使用情形

android7.0安装apk会并发的标题和原因

google在android7.0上述,幸免间接通过file://格局的uri向另三个应用传递文件uri。所以招致安装apk和相机拍照保存数据都会合世难题。google提供了FileProvider类来收获文件uri。FileProvider的精气神也正是ContentProvider。

1.拍照

7.0之前:

 public static void startTakePhoto(Activity activity) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(
                MediaStore.EXTRA_OUTPUT,
                Uri.fromFile(new File(Utils.IMAGE_FOLDER
                        + Utils.TEMP_IMAGE_NAME)));
        activity.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
    }

那个时候就算大家应用Android 7.0仍旧以上的原生系统,再度运维一下,你会发觉选择直接停止运行,抛出了android.os.FileUriExposedException:

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:// U中华VI。假如大器晚成项包蕴文件 U逍客I 的 intent 离开你的应用,则选择出现故障,并出现 FileUriExposedException 十分。

相通的,官方网址也付出了然决方案:

要在接受间分享文件,您应发送风度翩翩项 content:// U汉兰达I,并付与 U福特ExplorerI 不时寻访权限。进行此授权的最简便易行方法是运用 FileProvider 类。如需明白有关权限和共享文件的详细音信,请参阅分享文件。 https://developer.android.com/about/versions/nougat/android-7.0-changes.html#accessibility

一 描述

  1. 问题
    对于面向 Android 7.0 的施用,Android 框架实践的 StrictModeAPI 政策幸免在您的应用外部公开 file:// UEnclaveI。若是意气风发项包罗文件 UENCOREI 的 intent 离开你的接纳,则采纳现身故障,并现身FileUriExposedException分外
  2. 缓慢解决方案
    要在应用间共享文件,您应发送生龙活虎项 content://URI,并付与UEscortI 不常探访权限。举行此授权的最不难易行方法除了将targetSdkVersion改成24以下,正是利用 FileProvider类

官网对FileProvider描述:

FileProvider是ContentProvider的多少个优异子类,它经过创建内容来兑现与应用程序相关联的文本的平安分享:// Uri用于文书,并不是文件:/// Uri。

内容U凯雷德I允许你使用一时走访权限来予以读取和写入访问权限。当你创设包涵内容UTiguanI的Intent时,为了将内容UHighlanderI发送到客商端应用程序,仍可以调用Intent.setFlags()来增多权限。只要接到活动的仓库处于活动状态,顾客端应用程序就足以行使这个权限。对于要访谈服务的用意,只要服务正在周转,权限就可用。

比较之下,为了操纵对文本的访谈:/// Uri你必得校正底层文件的文件系统权限。您提供的权能可用以其余应用程序,并在你改革在此以前保持有效。这种访谈水平基本上是不安全的。

内容U兰德酷路泽I提供的加码文件访谈安全等第使FileProvider成为Android安全根底构造的主要部分。

简易回想下ContentProvider

ContentProvider做为android的第四次全国代表大会组件之后生可畏,它至关心重视要的机能正是夸进度通讯。从多个进度读取数据并提要求另三个进度。ContentProvider的底层是Binder机制。ContentProvider宗旨由八个部分构成

  1. uri: 统一能源标记符 格式 content://com.aaa.bbb/user/1 。即:合同://授权(authority卡塔尔国/路线/id。阐明了拜访哪个进度(com.aaa.bbb卡塔尔(قطر‎的哪些文件。相关的类有U揽胜极光IMather和ContentUri(在uri前面增加和得到id)。
  2. ContentResolver:用来帮忙试行ContentProvider中的增加和删除改查,简化操作。
  3. ContentOberser:监听有些uri下边数据变动,使用观看者情势。

创建三个ContentProvider步骤便是 清单文件注册provider - 成立provider类,重写增加和删除改查方法。访谈ContentProvider步骤就是 创立uri contentProvider ContentResolver - 调用api访谈。

怎么要回溯呢?因为FileProvider正是ContentProvider。那么使用FileProvider好处是何等?当然是加多开辟者专业量,裁减花钱时间。。 。 好处大约是: 防止过去的file://的点子向外界公开采访(小编估算是setUri(file://xxx卡塔尔那么些xxx文件就能够被赋予外界应用的拜会权限。不然怎么要换FileProviderState of Qatar,而由此FileProvider的措施付与临时权限。官方解释:

对于面向 Android 7.0 的利用,Android 框架实行的 StrictMode API 政策禁绝在你的施用外界公开 file:// U中华VI。要是大器晚成项包罗文件 UHavalI 的 intent 离开你的行使,则选择现身故障,并冒出 FileUriExposedException 十分。

要在运用间共享文件,您应发送大器晚成项 content:// UGL450I,并赋予 U凯雷德I 一时拜访权限。举办此授权的最轻便易行方法是利用 FileProvider 类。如需询问关于权限和共享文件的详细消息,请参阅分享文件。

2.使用FileProvider宽容拍照

FileProvider实际上是ContentProvider的贰个子类,它的效果也正如分明了,file:///Uri不给用,那么换个Uri为content://来顶替。

https://developer.android.com/reference/android/support/v4/content/FileProvider.html

完整的得以达成步骤:
(1)声明provider:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.zhy.android7.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

注意一点,他索要设置叁个meta-data,里面指向一个xml文件。

(2)编写resource xml file

<?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="name" path="path" />
     <external-cache-path name="name" 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()

各样节点都扶持三个属性:name , path

内需四个假造的渠道对文本路线举行映射,所以须要编写制定个xml文件,通过path以至xml节点鲜明可访谈的目录,通过name属性来映射真实的文件路线。

(3)使用FileProvider API

 public static void startTakePhoto(Activity activity) {
      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      Uri fileUri = FileProvider.getUriForFile(this, "com.lqwawa.internationalstudy.fileprovider", file);
     intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
     activity.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO);
    }

接下来再看一眼我们调换的uri:

content://com.lqwawa.internationalstudy.fileprovider/external/20170601-041411.png

能够看出格式为:content://authorities/概念的name属性/文件的相对路径,即name隐蔽了可存款和储蓄的文本夹路线。
今昔拿7.0的原新手提式无线话机运营就正常了

而是张开几个4.4的模拟器,运营上述代码,你会开掘又Crash啦,抛出了:Permission Denial~

Caused by: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{52b029b8 1670:com.android.camera/u0a36} (pid=1670, uid=10036) that is not exported from uid 10052
at android.os.Parcel.readException(Parcel.java:1465)
at android.os.Parcel.readException(Parcel.java:1419)
at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:2848)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:4399)

因为低版本的系列,仅仅是把那么些当成一个常常性的Provider在运用,而作者辈未有授权,contentprovider的export设置的也是false;引致Permission Denial。

那就是说,我们是或不是可以将export设置为true呢?

很可惜是无法的。

在FileProvider的内部:

@Override
public void attachInfo(Context context, ProviderInfo info) {
    super.attachInfo(context, info);

    // Sanity check our security
    if (info.exported) {
        throw new SecurityException("Provider must not be exported");
    }
    if (!info.grantUriPermissions) {
        throw new SecurityException("Provider must grant uri permissions");
    }

    mStrategy = getPathStrategy(context, info.authority);
}

确定了exported必须是false,grantUriPermissions必须是true ~~

所以唯大器晚成的方法就是授权了~

context提供了四个措施:

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

可以看见grantUriPermission须求传递几个包名,便是您给哪些应用授权,可是众多时候,比方分享,大家并不知道最后客商会采用哪位app,所以大家能够如此:

public void takePhotoNoCompress(View view) {
    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.galaxyschool.app.wawaschool.fileprovider", file);

        List<ResolveInfo> resInfoList = getPackageManager()
                .queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }

        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
        startActivityForResult(takePictureIntent, REQUEST_CODE_TAKE_PHOTO);
    }
}

这么就化解了,然而还是挺麻烦的,如若你仅仅是对旧种类做配合,照旧建议做一下版这么些高校验就能够,也正是说不要管怎样授权了,直接那样获取uri

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

二 如何运用FileProvider

大家先看如何接纳FileProvider,官方网站也会有详细表明:https://developer.android.com/reference/android/support/v4/content/FileProvider.html

本文由永利澳门平台发布于计算机资讯,转载请注明出处:永利皇宫463com:Android 7.0权限适配:FileUriExposedException异常

您可能还会对下面的文章感兴趣: