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. 必须是系统应用或具备系统权限,否则静默安装会失败。
相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
7177773 天前
研发平台选哪家比较好:2026年主流平台对比与Gitee选型指南
人工智能·gitee
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas