Android 使用 PackageInstaller 实现静默安装,并通过 BroadcastReceiver 自动重启应用

在 Android 系统中,如果你的应用具有系统权限(如系统签名或安装在 /system/priv-app 目录),就可以使用 PackageInstaller 实现 APK 的静默安装。

安装完成后,我们通常希望应用能够自动重启,以便更新立即生效。

本文将完整展示:

  1. BroadcastReceiver 监听安装事件

  2. 捕获自定义安装完成事件 (INSTALL_FINISH)

  3. 自动启动自身应用

  4. 使用 PackageInstaller 安装 APK 的完整代码

适用于 OTA 升级、企业设备更新、Kiosk 设备、医疗设备等定制系统。


一、监听系统安装广播

Android 在安装、升级应用时,会发送系统广播:

  • android.intent.action.PACKAGE_ADDED ------ 第一次安装

  • android.intent.action.PACKAGE_REPLACED ------ 覆盖安装(升级时必走)

  • 自定义广播:安装提交后的回调(PackageInstaller.commit)

清单文件配置如下:

复制代码
<receiver
    android:name=".updata.InstallReceiver"
    android:enabled="true"
    android:exported="true">

    <!-- 覆盖安装(升级) -->
    <intent-filter android:priority="999">
        <action android:name="android.intent.action.PACKAGE_REPLACED" />
        <data android:scheme="package" />
    </intent-filter>

    <!-- 第一次安装 -->
    <intent-filter android:priority="999">
        <action android:name="android.intent.action.PACKAGE_ADDED" />
        <data android:scheme="package" />
    </intent-filter>

    <!-- PackageInstaller commit 回调 -->
    <intent-filter>
        <action android:name="com.test.INSTALL_FINISH" />
    </intent-filter>

</receiver>

这里我们设置了高优先级,使其在系统广播链路中更早得到回调。


二、安装事件接收器 InstallReceiver

InstallReceiver 的任务:

  • 判断是否当前 App 被安装或升级

  • 若是,则自动启动

  • 接收 PackageInstaller 的提交回调

  • 同样执行启动逻辑

完整代码如下:

复制代码
package com.test.test.updata;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class InstallReceiver extends BroadcastReceiver {

    private static final String TAG = "========";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        String pkg = null;

        if (intent.getData() != null) {
            pkg = intent.getData().getSchemeSpecificPart();
        }
        Log.e("========", "收到广播:" + action + "   包:" + pkg);

        // 本应用首次安装或升级安装
        if ((Intent.ACTION_PACKAGE_ADDED.equals(action)
                || Intent.ACTION_PACKAGE_REPLACED.equals(action))
                && pkg != null
                && pkg.equals(context.getPackageName())) {

            Log.e("========", "捕获安装或升级 → 自动启动");
            launchSelf(context);
            return;
        }

        // PackageInstaller 回调(仅首次安装)
        if ("com.test.INSTALL_FINISH".equals(action)) {
            Log.e("========", "收到 PackageInstaller commit 回调");
            launchSelf(context);
        }
    }

    private void launchSelf(Context context) {
        Intent launch = context.getPackageManager()
                .getLaunchIntentForPackage(context.getPackageName());

        if (launch != null) {
            launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(launch);
        } else {
            Log.e(TAG, "找不到自己的启动 Activity");
        }
    }
}

安装完成后,会自动重新打开应用,实现无感升级。


三、使用 PackageInstaller 静默安装 APK

核心类:SystemInstaller

实现流程:

  1. 创建安装 Session

  2. 将 APK 内容写入 Session

  3. commit 提交安装

  4. 提交完成后系统会回调我们自定义广播 INSTALL_FINISH

代码如下:

复制代码
package com.test.test.updata;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;

public class SystemInstaller {

    private static final String TAG = "SystemInstaller";

    @SuppressLint("RequestInstallPackagesPolicy")
    public static void installApk(Context context, String apkPath) {
        try {
            File apkFile = new File(apkPath);
            if (!apkFile.exists()) {
                Log.e(TAG, "APK 文件不存在: " + apkPath);
                return;
            }

            PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();

            PackageInstaller.SessionParams params =
                    new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);

            params.setAppPackageName(null);

            int sessionId = packageInstaller.createSession(params);
            PackageInstaller.Session session = packageInstaller.openSession(sessionId);

            // 写入 APK
            try (OutputStream out = session.openWrite("app_install", 0, apkFile.length());
                 FileInputStream in = new FileInputStream(apkFile)) {

                byte[] buffer = new byte[8192];
                int c;
                while ((c = in.read(buffer)) != -1) {
                    out.write(buffer, 0, c);
                }
                session.fsync(out);
            }

            // 提交安装,发送 INSTALL_FINISH 回调
            session.commit(get(context).getIntentSender());
            session.close();

            Log.i(TAG, "提交安装成功");

        } catch (Exception e) {
            Log.e(TAG, "系统安装异常", e);
        }
    }

    public static PendingIntent get(Context context) {
        Intent intent = new Intent(context, InstallReceiver.class);
        intent.setAction("com.test.INSTALL_FINISH");

        return PendingIntent.getBroadcast(
                context,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE
        );
    }
}

四、安装完成自动重启应用的完整链路

整个流程如下:

复制代码
installApk()
     ↓ commit
InstallReceiver (INSTALL_FINISH 广播)
     ↓
launchSelf() 启动应用

系统安装广播 PACKAGE_REPLACED/PACKAGE_ADDED
     ↓
InstallReceiver 捕获自身包名
     ↓
launchSelf() 重新启动

无论是首次安装、覆盖安装还是 commit 回调,都能够完成自动启动。


五、注意事项

  1. 必须是系统应用或具备系统权限,否则静默安装会失败。
相关推荐
QING6181 天前
Kotlin 协程新手指南 —— 协程上下文与调度器
android·kotlin·android jetpack
潘潘潘1 天前
Android JAVA Socket 知识梳理
android
00后程序员张1 天前
Jenkins 自动上传 IPA 到 App Store 把发布步骤融入 CI/CD
android·ios·小程序·https·uni-app·iphone·webview
Gary Studio1 天前
复杂 SoC(RK3568)PCB 布局的五步
android·linux·硬件
plainGeekDev1 天前
HttpURLConnection → OkHttp + Kotlin
android·java·kotlin
QING6181 天前
Kotlin 协程新手指南 —— 协程基础与挂起函数
android·kotlin·android jetpack
2601_961766641 天前
【分享】分身空间 2.3.7[特殊字符]生活工作互不打扰
android·生活
百度搜知知学社1 天前
抖音双模块架构:兼容全安卓版本并支持登录
android·架构·安卓·登录·兼容性·抖音
文阿花1 天前
Echarts实现柱状3D扇形图
android·3d·echarts
故渊at1 天前
第六板块:Android 安全与权限体系 | 第十九篇:SELinux 强制访问控制与沙箱机制
android·安全·访问控制·selinux·权限体系·沙箱机制