Android 7.0/7.1获取已安装应用时触发PackageManager崩溃排查
Android 7.0/7.1 应用列表获取崩溃问题分析与解决
嘿,这个问题我之前在处理三星设备兼容性时碰到过,结合你给出的崩溃信息和三星Knox相关细节,咱们一步步拆解:
一、崩溃的核心原因
你遇到的PackageManager$NameNotFoundException,本质是两个因素共同作用的结果:
- flags带来的「幽灵包」:你设置的
flags包含了GET_UNINSTALLED_PACKAGES,这个标记会让getInstalledApplications()返回一些特殊包——比如已卸载但残留数据的应用,或是com.samsung.knox.knoxtrustagent这种三星系统级受保护组件。这些包能被系统枚举,但普通应用没有权限访问它们的详细信息。 - 7.x系统的权限限制:Android 7.0/7.1对系统受保护应用的访问控制比后续版本更严格,三星Knox作为企业级安全组件,其相关包的权限被系统锁定,你的应用调用
getApplicationInfo()时会直接抛出异常。这也是你在测试机上复现不了的原因——要么测试机没有这个特殊包,要么系统权限配置不同。
二、具体的规避方案
根据你的业务需求,可选择以下几种方案:
1. 移除不必要的标记(推荐)
如果你的业务不需要获取已卸载应用的信息,直接把GET_UNINSTALLED_PACKAGES从flags中移除,这样getInstalledApplications()就不会返回那些不可访问的包了:
private static final int flags = PackageManager.GET_META_DATA | PackageManager.GET_SHARED_LIBRARY_FILES;
2. 捕获异常并跳过问题包
如果必须保留GET_UNINSTALLED_PACKAGES,那就在捕获异常时直接跳过这个包,避免崩溃:
for (ApplicationInfo info : infos) { installedApps.add(info.packageName); try { ApplicationInfo ai = packageManager.getApplicationInfo(info.packageName, 0); // 你的正常处理逻辑 } catch (PackageManager.NameNotFoundException e) { // 记录日志并跳过该包 Log.w("ListApptest", "无法访问应用包:" + info.packageName, e); continue; } }
3. 增加非空检查(稳妥起见)
虽然你说getInstalledApplications()运行正常,但极端情况下(比如系统PackageManager服务异常)可能返回null,加个检查能避免空指针崩溃:
List<ApplicationInfo> infos = packageManager.getInstalledApplications(flags); if (infos == null || infos.isEmpty()) { return new ArrayList<>(); }
4. 针对性跳过三星Knox包(可选)
如果需要专门处理这类系统受保护包,可以提前判断包名直接跳过:
for (ApplicationInfo info : infos) { String pkgName = info.packageName; // 跳过所有三星Knox相关的受保护包 if (pkgName.startsWith("com.samsung.knox.")) { continue; } installedApps.add(pkgName); // 后续处理逻辑 }
三、额外的优化建议
- AsyncTask在Android 7.x之后已经被官方逐步废弃,如果你后续要兼容更高版本,建议改用Coroutines或者ExecutorService来实现异步任务,稳定性更好。
- 如果你未来要兼容Android 11+,记得申请
QUERY_ALL_PACKAGES权限(如果需要获取所有应用列表),不过在7.x版本中这个权限还不存在,不用考虑。
内容的提问来源于stack exchange,提问作者narb
相关产品推荐
相关产品推荐

