关于这个毛茸茸的小错误

最新在开发一个新的 APP ,自己手动写了版本更新,测试发现,覆盖安装的时候,在 Android 7.0 系统上出现解析包错误。

报错信息:

核心报错信息:

java.lang.SecurityException: Permission Denial:
opening provider android.support.v4.content.FileProvider from ProcessRecord{cc3ad2316425:
com.android.packageinstaller/u0a21} (pid=16425, uid=10021) that is not exported from uid 10340

关于安装失败,案发地点的代码:

/*******************错误版***************************/
/*** 安装 apk 文件* @param apkFile apk 文件*/
private void installApk(File apkFile){Intent intent = new Intent(Intent.ACTION_VIEW);Uri apkUri = null;//判断版本是否是 7.0 及 7.0 以上if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { apkUri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);//添加这一句表示对目标应用临时授权该Uri所代表的文件intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);} else {apkUri = Uri.fromFile(apkFile);}//由于没有在Activity环境下启动Activity,所以设置下面的标签intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setDataAndType(apkUri,"application/vnd.android.package-archive");startActivity(intent);
}

当发现是 7.0 系统上才会出现的问题之后,再联系报错信息,很容易就想到 FileProvider 的权限问题,然而并没有什么用,我还是不知道怎么回事。对比之前实现的版本更新的代码,定位到 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 这句代码,当把这句话放在前面,就是正常的。

/*** 安装 apk 文件* @param apkFile apk 文件*/
private void installApk(File apkFile){Intent intent = new Intent(Intent.ACTION_VIEW);//放在此处//由于没有在Activity环境下启动Activity,所以设置下面的标签intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);Uri apkUri = null;//判断版本是否是 7.0 及 7.0 以上if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { apkUri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);//添加这一句表示对目标应用临时授权该Uri所代表的文件intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);} else {apkUri = Uri.fromFile(apkFile);}intent.setDataAndType(apkUri,"application/vnd.android.package-archive");startActivity(intent);
}

错误原因

看一下核心报错

java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{cc3ad23 16425:com.android.packageinstaller/u0a21} (pid=16425, uid=10021) that is not exported from uid 10340

这个是权限不足的提示,Android 7.0 之后通过 FileProvider 共享文件,因为 exported为 false,所以需要给目标应用,授予临时权限,加上 Intent.FLAG_GRANT_READ_URI_PERMISSION 参数。

很明显,我已经加了,但是很不幸,我们在添加之后, intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 把添加的给替换掉了,因此实际上还是未给权限。

这是因为对 setFlags() 和 addFlags() 认知不清,错误使用导致的,最后,将 setFlags()操作放在 addFlags() 之前解决了这个问题。

intent.setFlags() 和 intent.addFlags() 的区别

setFlags():为intent 设置特殊的标志,会覆盖 intent 已经设置的所有标志。

 public Intent setFlags(int flags) {mFlags = flags;return this;}

addFlags():为intent 添加特殊的标志,不会覆盖,只会追加。

  public Intent addFlags(int flags) {mFlags |= flags;return this;}

拓展知识补充

Android 7.0 行为变更,通过 FileProvider 在应用中共享文件

Android 7.0 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更。其中有一条,对日常开发的适配影响是非常大的,传递软件包网域外的 file:// URI 可能给接收器留下无法访问的路径。因此,尝试传递 file:// URI 会触发 FileUriExposedException。解决方法是使用 FileProvider 将 file:// URI 转换为 content:// URI 。

FileProvider 的使用方法网上有很多,这里不多说了,附上 Google 官方文档 FileProvider

Intent.FLAG_GRANT_READ_URI_PERMISSION & android:grantUriPermissions & context.grantUriPermission()三者的区别

刚开始用 FileProvider 的时候,并不是很懂它,只是 copy & paste,所以对这三个 grantUriPermissions 很是傻傻分不清楚。遇到这么问题专门去查了一下。

android:grantUriPermissions

这个是定义在 FileProvider 中的属性,作用是允许对 ContentProvider 通信的目标应用临时授权。

这个属性默认是 false,但是要求必须设置为 true,否则直接报错 java.lang.SecurityException: Provider must grant uri permissions

我们可以在 FileProvider 源码中看到对这个值的限制:

  public void attachInfo(Context context, ProviderInfo info) {super.attachInfo(context, info);if(info.exported) {throw new SecurityException("Provider must not be exported");} else if(!info.grantUriPermissions) {throw new SecurityException("Provider must grant uri permissions");} else {this.mStrategy = getPathStrategy(context, info.authority);}}

Intent.FLAG_GRANT_READ_URI_PERMISSION

这个是 Intent 的一个 Flag 值,会临时授予目标引用对所传递 Uri 的临时访问权限。
这个临时访问权限在setResult()数据接收方 Activity 还在栈中时,都是有效的,如果弹出了,临时访问权限自动取消。

通过 addFlags()setFlags() 两种方式添加:

 //addFlags()方式intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//setFlags()方式intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

context.grantUriPermission()

这个方法和使用 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 可以任选其一,因为它们的效果是一样的,都是对目标引用临时授权。

区别在于 context.grantUriPermission()的临时访问权限不会在数据接收页面关闭时自动取消,只有手动调用 revokeUriPermission() 或者整个应用重新启动,才会失效。

grantUriPermission(String toPackage, Uri uri,@Intent.GrantUriMode int modeFlags)需要传递三个参数:

参数名称 参数含义
toPackage 你想要授权的目标应用,记住是目标应用
uri 传递的 Uri
modeFlags 需要的临时授予的权限们

使用方法:

//判断版本是否是 7.0 及 7.0 以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {apkUri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);//设置 intent 目标应用类型和传递的数据,这句要放在查询之前intent.setDataAndType(apkUri,"application/vnd.android.package-archive");//查询所有符合 intent 跳转目标应用类型的应用List<ResolveInfo> resInfoList = getActivity().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);//然后全部授权for (ResolveInfo resolveInfo : resInfoList) {String packageName = resolveInfo.activityInfo.packageName;getActivity().grantUriPermission(packageName, apkUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);}}

特别注意的地方:

我第一次测试使用这个方法的时候,报错了,报错信息是

java.lang.SecurityException: Permission Denial:
opening provider android.support.v4.content.FileProvider from ProcessRecord{cc3ad2316425:
com.android.packageinstaller/u0a21} (pid=16425, uid=10021) that is not exported from uid 10340

原因是我非常蠢地把 intent.setDataAndType() 放在了查询后面,所以 queryIntentActivities 没查到 com.android.packageinstaller 这个应用,也就没授权,大家使用的时候要注意顺序。

Intent.FLAG_ACTIVITY_NEW_TASK 的意义

Intent 的意思是意图,大白话翻译过来就是,定义即将要发生的动作。 使用 Intent ,可以在 Activity(前台页面), BroadcastReceiver(广播) 和 background Service (后台服务)之间做数据传递和其他交互。Flag 是 Intent 中扮演是辅助者的的角色,作用是添加相应的配置,让不同组件之间的交互更加方便。Android 也为 Intent 提供了很多 Flag, Intent 可以通过 setFlags() 或 addFlags() 添加一个或者多个 Flag,两个方法的区别上面说过了,这里就不啰嗦了。

Intent.FLAG_ACTIVITY_NEW_TASK 的含义是开启一个新的任务栈来装载目标页面。以这种方式启动activity,相当于目标 Activity 的 launchMode 属性设置 “singleTask” 。

注意如果试图从非 Activity 的非正常途径,比如从一个 Receiver 中启动一个 Activity,则 Intent 必须要添加 FLAG_ACTIVITY_NEW_TASK 标记。

如果是从 Activity 中跳转到 Activity ,会有以下效果。

写了一个小 Demo 测试,在 MainActivity 中通过 Intent 打开 SecondActivty,Intent 设置 FLAG_ACTIVITY_NEW_TASK。

    Intent intent = new Intent(MainActivity.this,SecondActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);

测试结果如下:

1.如果 SecondActivity 在 AndroidManifest.xml 没有设置 Task Affinity,创建的 SecondActivity 的栈和 MainActivity 的栈一样。

2.如果 SecondActivity 在 AndroidManifest.xml 设置 Task Affinity 属性,SecondActivty 才会创建在一个新栈中。

Task Affinity 属性导致表现不同的原因是,当使用 singleTask 方式启动一个Activity 时, 系统首先会查找有没有 Task Affinity 相同的栈存在,如果有存在,直接压入栈,没有则会重新创建。当没有设置时,默认 Task Affinity 是 packageName,所有的 Activity 的 Task Affinity 都一样,于是表现和 Standard Mode 没有区别。

写在最后

遇到这个问题,觉得很奇怪,有点解释不通,其实归根结底也是自己太菜了而已,很对东西都不知道,copy & paste 在日常工作中是常态,但终究不是学习之道,功能实现完还是要回来找到真正原因才会安心,也能查漏补缺。

与诸君共勉,加油。


欢迎关注个人微信公众号「浅浅同学的开发笔记」,最新的博客,好玩的事情,都会在上面分享,期待与你共同成长。

Android-7.0系统安装异常之解析包错误相关推荐

  1. Android 7.0 出现 ”FileUriExposedException“ 和 ”解析包出现错误“ 异常的解决办法...

    问题1 :android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.xxx.xxx.xxx.rel ...

  2. Android 7.0/8.0 安装APK时解析包错误问题

    在 Android 7.0 引入了"私有目录被限制访问",通过使用FileProvider来解决问题,但是在安装应用时却出现了解析包出错的问题 来检查一下代码 private vo ...

  3. vivo手机解析包错误解决方式

    作为一个用vivo手机的孩子 在真机测试时深受vivo手机的毒害  ,出现解析包错误  我就在百度各种查找 终于找到了有效的解决方案 studio中的具体操作 解决android真机测试,出现解析包错 ...

  4. Android应用解析包错误原因的总结(不定时更新)

    Android应用解析包错误的原因 导致这个问题的原因目前就我所知道的而言大致有三种(其他原因请看文章末尾): 一.使用v1.v2方式签名带来的问题 二.Android7.0新特性导致的原因 三.应用 ...

  5. android微信解析失败,为什么我的手机安装不上微信,一安装就说解析包错误

    为什么我的手机安装不上微信,一安装就说解析包错误以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 为什么我的手机安装不上微 ...

  6. Android 加固包之后,vivo手机安装时出现解析包错误的情况

    原因:因为我们跑的 run apk都是 debug 版本,也就是测试版本,而 vivo就不支持这个测试apk. 解决办法: gradle.properties文件中添加 android.injecte ...

  7. 版本更新时出现解析包错误

    问题场景是:软件从 1.2.4 升级到 1.3.0 的过程中出现了如题的问题即下载后安装时出现"解析包时出现问题"的错误后停止.而且不是所有的手机都会出现这个问题,在华为 2.2 ...

  8. mendelay为什么安装不了_方舟生存进化手游国际版出现解析包错误和解析包错误以及下载完无法安装这样应对...

    方舟生存进化手游国际版已于6月14日提前上线谷歌商城和苹果商店,相信许多小伙伴已经通过各种渠道下载到了游戏,部分小伙伴下载时会出现解析包错误以及下载完无法安装情况,今天小辰给大家带来最新的处理方法. ...

  9. 安装app提示解析包错误解决办法

    安卓10.0以上版本安装apk使用以下代码会提示:解析包错误 Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType ...

最新文章

  1. ubuntu18.04安装CUDA10.0
  2. 去重查询_《前端算法系列》数组去重
  3. Ubuntu 17 安装 tensorflow
  4. Java反射在整个程序运行中的位置
  5. DFT实训教程笔记2(bibili版本)- Scan synthesis practice
  6. .NET设计模式(5):工厂方法模式(Factory Method)
  7. 从编译到执行,C++如何开发SIMD友好的代码?
  8. linux mint 自动更新设置,如何升级到Linux Mint 19.1
  9. 成考期末计算机组成原理,2020年10月自考02318计算机组成原理真题及答案
  10. cocos2dx 3.x Node::schedule
  11. [导入]关于“啸聚一庐--txna”
  12. 全角半角英文字母及符号
  13. html中img图片绝对路径时无法正常显示的问题
  14. word中快速确认字体颜色的方法
  15. Bigemap支持百度地图
  16. RuoYi-Vue——Swagger文档401问题
  17. 红宝石、蓝宝石的主成份是什么?
  18. STM32 之十 供电系统及内部参照电压(VREFINT)使用及改善ADC参考电压,内部参照电压的具体方法,只有在STM32F0x芯片的参考手册中才能找到,其他MCU的参考手册都是很简单的说明
  19. 转发--目前开源数据集整理
  20. SOLIDWORKS Electrical端子排管理

热门文章

  1. 2022年全球市场液体石蜡总体规模、主要生产商、主要地区、产品和应用细分研究报告
  2. 易课寄在线购课系统开发笔记(二十五)--完成课程详情页面展示相关功能(应用Redis缓存)
  3. 病毒及攻击防御手册之四
  4. 计算机设备维护保养和网络巡检,弱电设备的维护保养及巡检管理制度
  5. HC-05蓝牙模块学习(两个蓝牙模块连接互发信息)
  6. 简述SpringMVC及其工作流程图
  7. 基于JAVA淮安市教育局职业教研室技能竞赛计算机毕业设计源码+系统+lw文档+部署
  8. 关于转行中医途径的一点思索
  9. 小米技术教父离职,雷军武大舍友在小米已所剩无几!
  10. 基于Java+SpringBoot+vue+element实现毕业就业招聘系统