记录一次解决安装 Apk 兼容性问题
问题描述
现象
代码执行安装Apk,出现系统弹框解析错误,解析包时出现错误
场景
在华为P20 Android 8.0 手机上,下载Apk并使用通知栏进度条显示,开启应用锁屏通知权限,下载过程在锁屏情况下进行,下载完成后自动执行安装Apk,在解锁后出现系统弹框,解析包出现错误。
解决之前安装Apk的方法
首先在AndroidManifest中声明fileProvider
<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_path" />
</provider>
provider属性说明
属性 | 说明 |
---|---|
name | android V4 包中的类FileProvider |
authorities | 你的文件的Uri的域名一般以包名.fileprovider的格式,防止重名 |
exported | 设置不允许导出,我们的FileProvider应该是私有的 |
grantUriPermissions | 允许获取文件的临时访问权限 |
resourse | 设置FileProvider访问的文件路径 |
res包下创建file_path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_path"
path="test" />
<cache-path
name="internal_path"
path="test" />
这里可以创建很多个paths,但是每个paths的name不能一样
</paths>
path 说明
<files-path name="*name*" path="*path*" /> 对应的是:Context.getFileDir()的路径地址
对应路径:Context.getFileDir()+"/${path}/"
得到路径:content://${applicationId}/&{name}/
<cache-path name="*name*" path="*path*" />
对应路径:Context.getCacheFir()+"/${path}/"
得到路径:content://${applicationId}/&{name}/
<external-path name="*name*" path="*path*" />
对应路径:Environment.getExternalStorageDirectory()+"/${path}/"
得到路径:content://${applicationId}/&{name}/
<external-files-path name="*name*" path="*path*" />
对应路径:Context.getExternalStorageDirectory()+"/${path}/"
得到路径:content://${applicationId}/&{name}/
<external-cache-path name="*name*" path="*path*" />
对应路径: Context.getExternalCacheDir()+"/${path}/"
得到路径:content://${applicationId}/&{name}/
举个例子说明:
path做如下声明
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
</paths>
File imagePath = new File(Context.getFilesDir(), "images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
contentUri值为:content://com.mydomain.fileprovider/my_images/default_image.jpg
public static void installApk(Context context,File apkFile){
try {
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(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
//添加对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} else {
apkUri = Uri.fromFile(apkFile);
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(apkUri,
"application/vnd.android.package-archive");
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGE" />
OK,以上就是大家普遍解决7.0,以及8.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
经过短暂的懵逼后,开始通过各种方式,探索问题的原因。
根据系统log分析,猜测在锁屏时,用于安装Apk的service处于休眠或者不可用的状态,导致通过intent.addflags
方式赋予的临时权限失效了。于是,再次仔细看了官方文档后,发现还有一个方法,可以生成权限且在主动调用方法或者手机重启后才会失效。
改进后的代码
public static void installApk(Context context,File apkFile){
try {
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(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
//添加对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} else {
apkUri = Uri.fromFile(apkFile);
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(apkUri,
"application/vnd.android.package-archive");
//查询所有符合 intent 跳转目标应用类型的应用,注意此方法必须放置setDataAndType的方法之后
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
//然后全部授权
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
再次尝试,此问题再没有出现。
原文发布时间为:2018-11-9
本文作者:bear~
本文来自云栖社区合作伙伴“安卓巴士Android开发者门户”,了解相关信息可以关注“安卓巴士Android开发者门户”。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
资政知识产权:打造自主品牌需要先了解商标要素及组合形式
《商标法》第八条 任何能够将自然人、法人或者其他组织的商品与他人的商品区别开的标志,包括文字、图形、字母、数字、三维标志、颜色组合和声音等,以及上述要素的组合,均可以作为商标申请注册。 一、文字商标 文字商标(WORD MARK)是指用汉字及其拼音字母或其他文字、字母组合而成,使用在商品或服务上的标志。文字商标是一种主要的商标形式,由于其便于呼叫,大多数企业都会首选文字作为商标注册和使用。作为商标的文字不一定是具有含义的文字,但至少可以识读。由于文字商标具有表达意思明确、视觉效果良好、易认易记等优点,所以,商标的设计越来越趋向文字化。 单独汉字构成的商标,如下图。商标名称一栏会写有文字商标的具体信息。 单独英文构成的商标,如下图。商标名称一栏会写有英文商标的具体信息。 二、图形商标 图形商标,是指以图形构成的商
- 下一篇
apk 反编译
在学习Android开发的过程你,你往往会去借鉴别人的应用是怎么开发的,那些漂亮的动画和精致的布局可能会让你爱不释手,作为一个开发者,你可能会很想知道这些效果界面是怎么去实现的,这时,你便可以对改应用的APK进行反编译查看。下面是我参考了一些文章后简单的教程详解。 测试环境: win 10 使用工具: CSDN上下载地址: apktool (资源文件获取) 下载[http://download.csdn.net/detail/vipzjyno1/7025111](http://download.csdn.net/detail/vipzjyno1/7025111) dex2jar(源码文件获取) 下载[http://download.csdn.net/detail/vipzjyno1/7025127](http://download.csdn.net/detail/vipzjyno1/7025127) jd-gui (源码查看) 下载[http://download.csdn.net/detail/vipzjyno1/7025145](http://download.csdn.net/d...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS关闭SELinux安全模块
- 2048小游戏-低调大师作品
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Red5直播服务器,属于Java语言的直播服务器
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Linux系统CentOS6、CentOS7手动修改IP地址