升级compileSdkVersion、targetSdkVersion至30后无法检测应用是否已安装
targetSdkVersion 30 以上应用安装判断的适配方案
报错根因
Android 11(API 30)正式上线包可见性权限机制,对应用可获取的其他应用信息做了强限制:
- 当
targetSdkVersion >= 30时,应用默认看不到设备上绝大多数第三方应用的包信息 - 直接调用
getPackageInfo()查询未声明的目标包名时,系统会直接抛出PackageManager.NameNotFoundException,不会返回null - 这也是为什么targetSdkVersion为29时代码运行正常,升级到30就报错的核心原因
具体适配操作
1. 清单文件声明包可见范围(必须)
在AndroidManifest.xml的<manifest>标签下添加<queries>节点,显式声明需要查询的目标应用包名,告知系统需要开放对应的包信息访问权限:
<manifest> <!-- 按需列出所有需要判断安装状态的应用包名 --> <queries> <!-- Facebook 正式版包名 --> <package android:name="com.facebook.katana" /> <!-- Twitter 正式版包名 --> <package android:name="com.twitter.android" /> </queries> <application> ... </application> </manifest>
注意:如果没有合理的业务场景,不要直接申请
android.permission.QUERY_ALL_PACKAGES全量查询权限,该权限会被Google Play及国内主流应用市场严格审核,非必要场景提交会被直接驳回。
2. 代码增加异常捕获做容错
getPackageInfo()方法在查询不到目标包时会直接抛出异常,不会返回null,需要用try-catch包裹调用逻辑,避免应用崩溃。修改后的参考代码如下:
String packageName = "com.facebook.katana"; PackageInfo installInfo = null; try { installInfo = context.getPackageManager().getPackageInfo(packageName, 0); } catch (PackageManager.NameNotFoundException e) { // 捕获异常说明目标应用未安装,直接走未安装分支 LogTool.c(TAG, "Target app " + packageName + " is not installed"); } if (installInfo != null && installInfo.applicationInfo.enabled) { LogTool.c(TAG, platform.name() + " is installed"); return true; } return false;
常见踩坑提醒
- 原有代码中对包信息对象的判空逻辑,在API 30+版本下对
getPackageInfo调用是冗余的:未查询到包时会直接触发异常,不会走到返回null的分支 - 如果目标应用是工作资料、多开分身下的应用,就算声明了
<queries>也可能存在查询不到的场景,需要结合实际业务做单独适配 - 如果是需要通过意图判断应用是否可响应的场景,除了包名声明,也可以在
<queries>下通过<intent>节点声明需要匹配的意图过滤器,适配性会更好
内容的提问来源于stack exchange,提问作者DavidR
相关产品推荐
相关产品推荐

