Android7.0后FileProvider升级安装包导致FileUriExposedException的异常

最近在鼓捣应用升级时遇到了安装失败的问题,抛出了“apk exposed beyond app through Intent.getData()”异常,网上一顿谷歌百度后晓得了,是谷歌在Android7.0(api 24)后出的幺蛾子。

Android7.0后Uri uri=Uri.fromFile(file) ——> Uri uri=FileProvider.getUriForFile(content,authority,file)获取uri文件地址的改变,使用FileProvider可以生成content://Uri来替代file://Uri

官方解答:出于用户隐私安全考虑,谷歌——(对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file://URI。传递软件包网域外的 file://URI 可能给接收器留下无法访问的路径。因此,尝试传递 file://URI会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider。)

查看原文,以下预览

 

fileerrorurl.png

出问题就开始解决吧

1.第一步当然你要访问文件是要权限的,这个在Android6.0后可要动态授权哦,这里不要忘了android.permission.REQUEST_INSTALL_PACKAGES这个权限,好多文章也没有提及,踩坑良久,哭一个。

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

2.在清单文件中注册一个名叫FileProvider 的ContentProvider,<meta-data> 子元素指向一个 XML 文件,用于指定要共享的目录。

   <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.bo.view.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <!-- android:authorities="应用包名.fileprovider"-->
            <!--元数据-->
            <!--处理file:// URI安装apk异常,因为Android7.0又引入“私有目录被限制访问”,“StrictMode API 政策”-->
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
   </provider>

3.上面的resource属性的资源文件,在res下新建一个xml文件夹,最后在xmll文件夹中新建file_paths.xml文件。

<resources>
    <paths>
        <external-cache-path path="myApkFile/" name="apk"/>
    </paths>
    <!--path:需要临时授权访问的路径(.代表所有路径),属性值不能使用具体的独立文件名,只能是目录名-->
    <!--name:就是给 path 属性所指定的子目录名称取一个别名,后续生成 content:// URI 时,
             会使用这个别名代替真实目录名。这样做的目的,是为了提高安全性-->

    <!--外部存储又分为SD卡和扩展卡内存,此处外部存储空间对应为内置SD卡内存-->
    <!--内部存储,我们称为InternalStorage,外部存储我们称为ExternalStorage-->

    <!--<files-path>:内部存储空间应用私有目录下的 files/ 目录,等同于 Context.getFilesDir() 所获取的目录路径;-->
    <!--<cache-path>:内部存储空间应用私有目录下的 cache/ 目录,等同于 Context.getCacheDir() 所获取的目录路径;-->
    <!--<external-path>:外部存储空间根目录,等同于 Environment.getExternalStorageDirectory() 所获取的目录路径;-->
    <!--<external-files-path>:外部存储空间应用私有目录下的 files/ 目录,等同于 Context.getExternalFilesDir(null) 所获取的目录路径;-->
    <!--<external-cache-path>:外部存储空间应用私有目录下的 cache/ 目录,等同于 Context.getExternalCacheDir();-->
</resources>

<external-cache-path> 表示应用程序内部SD卡存储目录下的 cache/ 目录,共享的完整路径为/storage/emulated/0/Android/data/com.bo.view/cache/myApkFile/;如果有多个需要共享的文件则添加多个。 path 属性用于指定apk所在的文件夹名,与步骤4中设置的对应,不要弄错了。 name 属性告诉 FileProvider 为共享路径创建一个别名为 apk 的路径字段,可任意。 想要通过 FileProvider 为文件生成 content URI 只能在此处指定目录,除此之外还可以共享其它目录,其标签对应的路径如下:

 

file_path.png

4.最后代码中调用安装apk,此处要判断区分7.0以后和之前版本,因为Android7.0之前使用FileProvider这个方式也会异常。有一点需要注意 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)必须在 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)前编码,否则setFlags()会覆盖addFlags()。

    /**
     * 下载成功后安装apk
     *
     * @param file 安装文件
     */
    public static void installApk( Context context) {
        //此处file应为apk下载的完整路径文件,这里下载在了外部存储的私有缓存文件夹下,
        // 你可以选择其他存储地址如:外置SD卡等,但是要注意与file_paths.xml中<external-cache-path>,<files-path>......里的
        //path="myApkFile/" 属性共享的文件夹目录对应一致
        File file= new File(context.getExternalCacheDir().getPath()+File.separator+"myApkFile", "my.apk");
        Intent intent = new Intent(Intent.ACTION_VIEW);
        // 由于没有在Activity环境下启动Activity,设置下面的标签
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //判读版本是否在7.0以上
            // 参数1 上下文, 参数2 Provider主机地址 和清单配置文件中保持一致 
            // 参数2 = android:authorities="应用包名.fileprovider"属性值
            // 参数3 = 上一步中共享的apk文件
            Uri apkUri = FileProvider.getUriForFile(context, "com.bo.view.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);
    }

最后附上参考文章:

Android 7.0 FileUriExposedException 的处理

下载安装APK(兼容Android7.0)

Android文件存储知识

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
状态栏的问题是什么?为什么在7.0以上的手机上运行时感觉没有沉浸式状态栏了?如何解决这个问题? 回答: 在Android 7.0以上的手机上,出现了一个问题,即无论怎么修改状态栏背景色都没有效果,看起来不再是沉浸式的状态栏。这个问题的原因是在Android 7.0之前,状态栏的实现是通过DecorView这个类来完成的,而在7.0以上,DecorView变成了一个独立的类,并且增加了一些新的属性和方法。其中一个新的属性是mSemiTransparentStatusBarColor,这个属性可能与我们要解决的问题有关。\[1\]\[2\]\[3\] 要解决这个问题,我们可以尝试修改mSemiTransparentStatusBarColor属性的值,以达到我们想要的状态栏效果。具体的实现方法可能需要根据具体的应用程序和代码来进行调整。可以通过查看相关的文档和源代码来了解如何正确地修改这个属性。 #### 引用[.reference_title] - *1* *2* [android状态栏半透明灰色,快速解决Android7.0下沉浸式状态栏变灰的问题](https://blog.csdn.net/weixin_35792040/article/details/117493339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [android状态栏半透明灰色,Android7.0沉浸式状态栏蒙灰问题完美解决](https://blog.csdn.net/weixin_34493827/article/details/117493345)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值