使用PackageInstaller安装APK在Android API 35设备上失败
针对API 35模拟器中PackageInstaller安装APK失败、抛出DOWNLOAD_FILE_NOT_FOUND_EXCEPTION的问题,结合API 35的权限与系统校验变化,给出以下解决方向:
修正Uri权限传递逻辑
API 35强化了文件访问权限校验,即便持有WRITE_EXTERNAL_STORAGE和REQUEST_INSTALL_PACKAGES权限,传递APK文件Uri时也必须显式授予读权限。在启动安装流程的Intent中添加FLAG_GRANT_READ_URI_PERMISSION,同时创建PackageInstaller Session时,通过ContentResolver打开InputStream读取APK,而非直接使用文件路径,避免系统扫描时无法访问文件。迁移APK到应用私有目录
将APK文件存储到应用私有目录(如getFilesDir()或getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)),这类目录无需额外存储权限,且系统校验时能稳定访问文件。避免使用外部公共存储目录,API 35对公共目录的访问限制更严格。彻底关闭应用验证机制
仅在Play Store中禁用Play Protect可能无效,可尝试在PackageInstaller的SessionParams中设置setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED);同时检查模拟器系统设置中的「应用验证」开关,确保完全关闭。若仍无效,可临时通过反射禁用VerifyApps服务(注意:反射方式不适合正式环境)。检查Session创建的流处理
确保PackageInstaller Session的写入流未提前关闭,正确流程示例:PackageInstaller installer = getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); int sessionId = installer.createSession(params); try (PackageInstaller.Session session = installer.openSession(sessionId); OutputStream out = session.openWrite("apk", 0, -1); InputStream in = getContentResolver().openInputStream(apkUri)) { byte[] buffer = new byte[1024 * 1024]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } session.fsync(out); // 提交安装 session.commit(PendingIntent.getBroadcast(this, 0, new Intent("INSTALL_COMPLETE"), 0).getIntentSender()); } catch (IOException e) { installer.abandonSession(sessionId); e.printStackTrace(); }必须通过ContentResolver获取InputStream,确保系统能正常读取到APK文件。
排查模拟器镜像问题
API 35的部分模拟器镜像可能存在VerifyApps服务的bug,尝试更新Android Studio到最新版本,更换Google Play版或纯Android版的API 35镜像重新测试。
内容的提问来源于stack exchange,提问作者csname1910

