PackageManager.getChangedPackages始终返回NULL,无法监测应用安装更新求助
解决Android Oreo+中getChangedPackages返回null的问题
兄弟,我太懂你这困扰了!Android Oreo之后系统对后台操作和包变更监测的限制真的严了不少,getChangedPackages这个方法确实变得不太靠谱,经常会返回null,我之前踩过这个坑,给你分享几个可行的解决方案:
先说说getChangedPackages为啥会失效
- 系统后台限制:Oreo开始对后台应用的资源访问做了严格管控,你的定时服务如果是普通后台服务,很可能被系统回收,导致无法持续获取变更记录;就算服务活着,系统也可能不会给后台应用完整的包变更数据。
- 序列号使用错误:这个方法需要传入上一次调用返回的序列号,要是初始序列号传错(比如一直传0),或者没正确更新后续的序列号,也会直接返回null。
靠谱的替代方案
方案1:动态注册包变更广播
Oreo及以上不允许静态注册ACTION_PACKAGE_ADDED、ACTION_PACKAGE_REPLACED这类广播,但我们可以在服务或Activity里动态注册,这样能实时收到包变更通知:
// 定义广播接收器 private BroadcastReceiver packageChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_PACKAGE_ADDED.equals(action) || Intent.ACTION_PACKAGE_REPLACED.equals(action)) { Uri data = intent.getData(); String packageName = data.getSchemeSpecificPart(); // 这里处理新增/更新的应用逻辑 Log.d("PackageMonitor", "检测到应用变更:" + packageName); } } }; // 在服务启动时注册广播 @Override public void onCreate() { super.onCreate(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REPLACED); filter.addDataScheme("package"); registerReceiver(packageChangeReceiver, filter); } // 服务销毁时务必注销广播,避免内存泄漏 @Override public void onDestroy() { super.onDestroy(); unregisterReceiver(packageChangeReceiver); }
注意:如果是后台服务,建议改成前台服务,不然很容易被系统杀死。
方案2:用JobScheduler监听包变更(符合Android规范)
从Android N开始,JobScheduler支持监听内容Uri,系统会在包发生变更时自动触发你的JobService,这是最符合Oreo+后台规范的方式:
// 构建JobInfo ComponentName jobComponent = new ComponentName(this, PackageChangeJob.class); JobInfo jobInfo = new JobInfo.Builder(1001, jobComponent) .setTriggerContentUri(new JobInfo.TriggerContentUri( Uri.parse("content://com.android.vending.cursor.myapps"), JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)) .build(); // 调度Job JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); jobScheduler.schedule(jobInfo);
然后实现JobService:
public class PackageChangeJob extends JobService { @Override public boolean onStartJob(JobParameters params) { // 在这里执行包变更检测逻辑 checkUpdatedApps(); jobFinished(params, false); return false; } @Override public boolean onStopJob(JobParameters params) { return false; // 不需要重试 } private void checkUpdatedApps() { // 这里可以获取所有应用列表对比上次记录,或者直接处理变更 PackageManager pm = getPackageManager(); // ... 你的检测逻辑 } }
方案3:对比应用列表(兜底方案)
如果上面的方法都有适配问题,那就用最稳妥的方式:每隔一段时间(推荐用WorkManager定时执行)获取所有已安装应用的列表,和上次保存的列表对比,找出新增或更新的应用:
private void checkForAppChanges() { PackageManager pm = getPackageManager(); List<ApplicationInfo> allApps = pm.getInstalledApplications(PackageManager.GET_META_DATA); // 从SharedPreferences读取上次保存的应用版本信息 SharedPreferences sp = getSharedPreferences("AppVersions", MODE_PRIVATE); Map<String, ?> lastVersions = sp.getAll(); for (ApplicationInfo app : allApps) { try { PackageInfo pkgInfo = pm.getPackageInfo(app.packageName, 0); int currentVersion = pkgInfo.versionCode; String pkgName = app.packageName; if (!lastVersions.containsKey(pkgName)) { // 新增应用 handleNewApp(pkgName); } else if ((int) lastVersions.get(pkgName) != currentVersion) { // 更新应用 handleUpdatedApp(pkgName); } // 保存当前版本号 sp.edit().putInt(pkgName, currentVersion).apply(); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } }
这个方法虽然没有实时性,但胜在稳定,适合对实时要求不高的场景。
额外注意事项
- 权限要求:Android 11及以上,要获取所有应用信息需要声明
QUERY_ALL_PACKAGES权限(在Manifest中添加),不过要注意Google Play的审核规则,要是你的应用有明确的需求理由,一般会通过。 - 后台存活:尽量用WorkManager、JobScheduler这类系统推荐的组件,避免用普通后台服务,不然很容易被系统回收。
内容的提问来源于stack exchange,提问作者The Matrix
相关产品推荐
相关产品推荐

