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. 必须是系统应用或具备系统权限,否则静默安装会失败。
相关推荐
张小姐的猫4 小时前
【MySQL】访问 & 数据流动原理
android·数据库·mysql·adb·1024程序员节
朱涛的自习室7 小时前
212 个需求、22 万行代码:Agent 接管开源项目的 19 天
android·前端·人工智能
传奇开心果12 小时前
【Flutter踩坑排雷】第3课:const 误用坑|const 错误使用引发 UI 异常与性能损耗
android·flutter
哲鲤12 小时前
AOSP思绎|Android 多媒体:一段视频为什么需要这么多服务
android
阿杰m12 小时前
Android源码第三章:Handler、Looper 与 MessageQueue——一条消息如何入队、唤醒并执行
android
海雅达手持终端PDA15 小时前
巡线、深井、爆管抢修与极寒水网的硬件矩阵怎么配?海雅达水务巡检终端PDA选型指南
android·物联网·5g·集成测试·能源·政务
恋猫de小郭17 小时前
Flutter 开始支持 Jetpack Compose Platform View
android·前端·flutter
三少爷的鞋1 天前
Android 架构七条铁律:架构师真正设计的不是代码,而是秩序
android
xiangxiongfly9151 天前
Android GsonFactory总结
android·gson·gsonfactory
WythePlus1 天前
Android 14 SELinux策略深度解析
android·安全策略·selinux策略