【Android】关于MY_PACKAGE_REPLACED广播

曾经遇到过一个问题,app里注册了MY_PACKAGE_REPLACED,用来识别app的更新行为,但是

静态注册的MY_PACKAGE_REPLACED,没有找到receiver

01-04 09:47:53.798 1000 2007 2379 V ActivityManager: Broadcast: Intent { act=android.intent.action.MY_PACKAGE_REPLACED flg=0x4000010 pkg=com.example.testapp (has extras) } ordered=false userid=0

01-04 09:47:53.800 1000 2007 2379 I ActivityManager: test====== collectReceiverComponents

01-04 09:47:53.800 1000 2007 2379 I PackageManager: test=====, comp == null

01-04 09:47:53.800 1000 2007 2379 I PackageManager: test=====, pms,pkgName=com.example.testapp

01-04 09:47:53.800 1000 2007 2379 I ActivityManager: test======,end, receivers is null

01-04 09:47:53.800 1000 2007 2379 V ActivityManager: Enqueueing broadcast: android.intent.action.MY_PACKAGE_REPLACED replacePending=false

可能是什么地方屏蔽了。

这里来学习一下MY_PACKAGE_REPLACED广播

MY_PACKAGE_REPLACED 是 Android 系统中一个非常有用但常被忽略的隐式广播(Implicit Broadcast) ,用于通知应用 "自身已被更新(替换)"。下面从原理、使用场景、限制和最佳实践全面讲解。


📡 一、什么是 MY_PACKAGE_REPLACED

  • Action 名称

    复制代码

    java

    编辑

    复制代码
    Intent.ACTION_MY_PACKAGE_REPLACED
    // 值为 "android.intent.action.MY_PACKAGE_REPLACED"
  • 触发时机

    当前应用被升级(覆盖安装) 时,系统会向该应用发送此广播。

  • 关键特点

    • 只有 被更新的应用自己 能收到
    • 不会被其他应用收到(安全设计)
    • 应用的新版本代码启动前 发送

✅ 与 PACKAGE_REPLACED 的区别:

  • PACKAGE_REPLACED:所有应用都能监听任意包的更新(需权限 + Android 8.0+ 受限)
  • MY_PACKAGE_REPLACED仅自己能监听自己的更新 ,无需额外权限,Android 8.0+ 仍可用

🛠 二、如何注册接收?

方式 1:动态注册(推荐)

复制代码

java

编辑

复制代码
public class MainActivity extends AppCompatActivity {
    private BroadcastReceiver updateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intent.getAction())) {
                Log.d("Update", "App has been updated!");
                // 执行更新后逻辑
                handleAppUpdate();
            }
        }
    };

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(updateReceiver, new IntentFilter(Intent.ACTION_MY_PACKAGE_REPLACED));
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(updateReceiver);
    }
}

方式 2:静态注册(AndroidManifest.xml)

复制代码

xml

编辑

复制代码
<receiver android:name=".MyUpdateReceiver" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
    </intent-filter>
</receiver>

强烈建议使用静态注册

因为 MY_PACKAGE_REPLACED 广播在应用首次启动前就可能发出,动态注册可能错过。


🎯 三、典型使用场景

场景 1:数据库版本迁移

  • 新版本修改了数据库结构
  • onReceive() 中检查旧版本号,执行升级脚本

场景 2:清除过期缓存或临时文件

复制代码

java

编辑

复制代码
public void onReceive(Context context, Intent intent) {
    // 删除旧版缓存(路径可能已变更)
    context.getCacheDir().deleteRecursively();
}

场景 3:重置 SharedPreferences 配置

  • 如果新版本改变了配置项含义,可选择重置
复制代码

java

编辑

复制代码
context.getSharedPreferences("config", Context.MODE_PRIVATE)
       .edit().clear().apply();

场景 4:重新注册推送 Token

  • 某些推送 SDK 要求每次更新后重新绑定设备

场景 5:显示"更新日志"弹窗

  • 首次启动新版本时,提示用户新功能

💡 注意:不要在此广播中做耗时操作(运行在主线程)!


⚠️ 四、重要限制与注意事项

1. 仅在"覆盖安装"时触发

  • ✅ 触发:adb install -r app.apk、Google Play 更新
  • ❌ 不触发:
    • 首次安装(无旧版本)
    • 卸载后重新安装
    • 通过 pm install -r 但包名不同

2. Android 8.0+(API 26)隐式广播限制

  • 大多数隐式广播(如 PACKAGE_REPLACED)被禁止静态注册
  • MY_PACKAGE_REPLACED 是例外!
    Google 明确将其列入 豁免列表

✅ 所以即使 targetSdkVersion >= 26,静态注册依然有效

3. 广播在新版本上下文中运行

  • 收到广播时,运行的是新 APK 的代码
  • 可安全访问新版本的资源、类、SharedPreferences

4. 不要启动 Activity(可能被系统拦截)

  • Android 10+ 限制后台启动 Activity

  • 如需引导用户,建议发通知:

    复制代码

    java

    编辑

    复制代码
    NotificationManager nm = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
    nm.notify(1, buildUpdateNotification());

🔍 五、与其他更新广播对比

广播 Action 谁能接收 是否需要权限 Android 8.0+ 静态注册
MY_PACKAGE_REPLACED 仅自己 ❌ 不需要 ✅ 允许
PACKAGE_REPLACED 所有应用 ❌ 不需要 ❌ 禁止(除非动态注册)
PACKAGE_ADDED 所有应用 ❌ 不需要 ❌ 禁止
PACKAGE_REMOVED 所有应用 ❌ 不需要 ❌ 禁止

✅ 结论:监听自身更新,请永远用 MY_PACKAGE_REPLACED


🧪 六、调试技巧

1. 模拟应用更新

复制代码

bash

编辑

复制代码
# 先安装 v1
adb install old_version.apk

# 再覆盖安装 v2
adb install -r new_version.apk

观察 logcat 是否收到广播。

2. 查看广播是否注册成功

复制代码

bash

编辑

复制代码
adb shell dumpsys package com.your.package | grep -A 5 MY_PACKAGE_REPLACED

3. 日志验证

复制代码

java

编辑

复制代码
Log.d("Broadcast", "Received MY_PACKAGE_REPLACED at " + System.currentTimeMillis());

✅ 七、最佳实践总结

建议 说明
使用静态注册 确保不丢失首次更新广播
避免耗时操作 广播在主线程,用 HandlerWorkManager 延迟处理
不要启动 Activity 改用通知或下次启动时检测版本
结合版本号判断 在 SharedPreferences 中记录上次版本,避免重复执行
测试覆盖安装流程 确保升级逻辑可靠

示例:带版本检查的更新处理

复制代码

java

编辑

复制代码
public class AppUpdateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String currentVersion = BuildConfig.VERSION_NAME;
        SharedPreferences prefs = context.getSharedPreferences("meta", Context.MODE_PRIVATE);
        String lastVersion = prefs.getString("last_version", "");

        if (!currentVersion.equals(lastVersion)) {
            // 执行一次性更新逻辑
            migrateData(context);
            prefs.edit().putString("last_version", currentVersion).apply();
        }
    }
}

💡 总结

  • MY_PACKAGE_REPLACED监听自身应用更新的最安全、最可靠方式
  • 无需权限,Android 8.0+ 仍支持静态注册
  • 适用于:数据迁移、缓存清理、配置重置、更新提示等场景
  • 记住:它只在"覆盖安装"时触发,首次安装不会收到
相关推荐
白雪落青衣29 分钟前
buuoj course 1详细解析
android
恋猫de小郭1 小时前
Android 发布全新性能分析器,实用性和性能大升级
android·前端·flutter
Kapaseker1 小时前
为什么 Java 的数组需要 new 出来
android·java·kotlin
黄林晴1 小时前
颠覆开发!Google AI Studio 一句话生成原生 Android App
android·google io
恋猫de小郭1 小时前
Flutter 3.44 发布啦,超级大版本更新!!!
android·flutter·ios
zb200641201 小时前
Laravel10.x重磅升级:新特性全解析
android
2601_957418802 小时前
深入解析Android相机有线连接:PTP与MTP协议栈实现原理与实践
android·数码相机·智能手机
努力努力再努力wz2 小时前
【QT入门系列】QWidget 六大常用属性详解:windowOpacity、cursor、font、focus、toolTip 与 styleSheet
android·开发语言·数据结构·c++·qt·mysql·算法
撩得Android一次心动2 小时前
C语言基础笔记3【个人用】
android·c语言·开发语言·笔记
小离a_a2 小时前
uniapp小程序封装圆环显示比例数据
android·小程序·uni-app