Android APK更新后无安装完成界面问题及PackageInstaller使用咨询
先说说你遇到的情况:用旧的Intent方式在低版本Android上安装APK,完成后能正常弹出带「完成」「打开」的选择界面;但升到Android N及以上后,用ACTION_INSTALL_PACKAGE安装,看似应用崩溃,还返回RESULT_CANCELED,也没那个选择界面。后来试了PackageInstaller,APK装好了,但同样没弹出「完成/打开」按钮。我来给你捋捋怎么解决这两个问题。
一、修正Intent安装方式,让高版本系统也显示选择界面
你原来的高版本代码用了ACTION_INSTALL_PACKAGE,其实换个思路,统一用ACTION_VIEW配合FileProvider,就能让高低版本都弹出系统默认的安装完成界面。修改后的代码如下:
Intent openIntent; File file = new File(mApkPath); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { String auth = mContext.getPackageName() + ".file.provider"; Uri apkUri = FileProvider.getUriForFile(mContext, auth, file); openIntent = new Intent(Intent.ACTION_VIEW); // 统一设置APK类型 openIntent.setDataAndType(apkUri, "application/vnd.android.package-archive"); // 添加必要的启动Flags openIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { openIntent = new Intent(Intent.ACTION_VIEW); openIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); openIntent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); } // 注意:如果在Activity中启动,API29及以上用registerForActivityResult,低版本用startActivityForResult // 非Activity环境必须保留FLAG_ACTIVITY_NEW_TASK return openIntent;
为什么这样改?因为ACTION_VIEW会调用系统默认的包安装器,这个安装器在所有版本里都会在安装完成后显示「完成/打开」的选择界面;而ACTION_INSTALL_PACKAGE更偏向后台安装场景,默认不会触发这个界面。另外你之前返回RESULT_CANCELED的问题,大概率是高版本代码没加FLAG_ACTIVITY_NEW_TASK,非Activity上下文启动Intent缺少这个Flag会导致启动失败,看起来像崩溃。
二、PackageInstaller的正确用法,实现「完成/打开」界面
用PackageInstaller的话,系统默认不会弹出安装完成的选择界面——它是更底层的API,需要我们自己监听安装结果,手动实现这个界面。完整流程如下:
1. 准备安装Session并写入APK
PackageInstaller installer = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); int sessionId = installer.createSession(params); PackageInstaller.Session session = null; try { session = installer.openSession(sessionId); // 将APK写入安装Session OutputStream outputStream = session.openWrite("apk", 0, -1); FileInputStream inputStream = new FileInputStream(new File(mApkPath)); byte[] buffer = new byte[1024 * 1024]; int len; while ((len = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, len); } session.fsync(outputStream); inputStream.close(); outputStream.close(); // 准备安装完成后的回调广播 Intent intent = new Intent(context, InstallResultReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( context, sessionId, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); // 提交Session,启动用户确认安装流程 session.commit(pendingIntent.getIntentSender()); } catch (IOException e) { e.printStackTrace(); if (session != null) { session.abandon(); } }
2. 创建广播接收器监听安装结果
创建InstallResultReceiver类,接收安装完成的状态:
public class InstallResultReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int resultCode = getResultCode(); if (resultCode == PackageInstaller.STATUS_SUCCESS) { // 获取新安装APP的包名(需提前知晓或从Intent解析) String packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME); // 弹出自定义「完成/打开」对话框 showInstallSuccessDialog(context, packageName); } else { // 处理安装失败场景 String errorMsg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); Toast.makeText(context, "安装失败:" + errorMsg, Toast.LENGTH_SHORT).show(); } } private void showInstallSuccessDialog(Context context, String packageName) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("安装成功"); builder.setMessage("应用已成功安装"); // 「打开」按钮:启动新安装的APP builder.setPositiveButton("打开", (dialog, which) -> { PackageManager pm = context.getPackageManager(); Intent launchIntent = pm.getLaunchIntentForPackage(packageName); if (launchIntent != null) { context.startActivity(launchIntent); } dialog.dismiss(); }); // 「完成」按钮:关闭对话框 builder.setNegativeButton("完成", (dialog, which) -> dialog.dismiss()); builder.show(); } }
3. 关键注意事项
- 记得在
AndroidManifest.xml中注册广播接收器:
<receiver android:name=".InstallResultReceiver" android:exported="false"/>
- Android 12及以上创建PendingIntent时,必须添加
FLAG_IMMUTABLE(或FLAG_MUTABLE,根据业务需求),否则会抛出权限异常。 - 你之前得到
STATUS_PENDING_USER_ACTION是正常状态,这表示安装流程需要用户确认(比如授权安装未知来源应用),用户操作后才会继续安装,直到返回STATUS_SUCCESS。
这样处理后,安装成功就能弹出和系统默认体验一致的「完成/打开」对话框。
内容的提问来源于stack exchange,提问作者thirtyyuan

