Android O设备闲置后收不到ACTION_SCREEN_ON广播及Widget更新异常求助
嘿,这个问题我太熟了——Android 8.0(API 26)给后台行为加了一堆限制,刚好踩中了你遇到的场景!先给你拆解下根源:
从API26开始,系统直接禁止了静态注册接收隐式广播,ACTION_SCREEN_ON和ACTION_USER_PRESENT都属于这类广播。之前你用SDK25的时候没问题,是因为当时还没这个限制;现在升级到targetSdk26,哪怕Widget还在主屏幕上,只要应用进程被杀,静态注册的广播接收器完全不会被系统唤醒,自然触发不了你的JobService更新Widget。
下面给你几个针对性的解决方案,按推荐程度排序:
方案1:用WorkManager替代JobService(推荐)
WorkManager是Jetpack推出的任务调度组件,比原生JobService更灵活,还能跨版本兼容,关键是即使应用被杀,满足条件时任务依然会执行。你可以给任务设置「设备退出空闲状态」的约束,刚好对应用户解锁手机的场景:
// 构建任务约束:设备不处于空闲状态(解锁后设备会退出空闲) Constraints constraints = new Constraints.Builder() .setRequiresDeviceIdle(false) .setRequiresBatteryNotLow(true) // 可选,避免低电量时消耗资源 .build(); // 创建单次更新任务 OneTimeWorkRequest widgetUpdateWork = new OneTimeWorkRequest.Builder(WidgetUpdateWorker.class) .setConstraints(constraints) .build(); // 在需要触发的时机(比如Widget添加、应用启动、动态广播收到解锁事件时)入队任务 WorkManager.getInstance(context).enqueue(widgetUpdateWork);
然后在WidgetUpdateWorker里完成数据拉取和Widget更新:
public class WidgetUpdateWorker extends Worker { public WidgetUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @NonNull @Override public Result doWork() { // 1. 调用后端接口获取最新数据 // 2. 更新Widget AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext()); int[] widgetIds = appWidgetManager.getAppWidgetIds( new ComponentName(getApplicationContext(), YourWidgetProvider.class) ); for (int id : widgetIds) { RemoteViews views = new RemoteViews(getApplicationContext().getPackageName(), R.layout.widget_layout); // 把新数据设置到views上 appWidgetManager.updateAppWidget(id, views); } return Result.success(); } }
方案2:结合动态广播+Widget系统回调
如果需要更实时的解锁更新,可以在应用存活时动态注册ACTION_USER_PRESENT广播,收到后立即触发更新;同时利用Widget的系统回调兜底(比如用户解锁后系统刷新Widget时):
动态注册广播(仅应用存活时有效)
在你的主Activity或者前台Service里注册:
private BroadcastReceiver userPresentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) { // 直接触发Widget更新或者Work任务 updateWidgets(context); } } }; @Override protected void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT); registerReceiver(userPresentReceiver, filter); } @Override protected void onPause() { super.onPause(); unregisterReceiver(userPresentReceiver); }
利用WidgetProvider的onUpdate兜底
当系统刷新Widget时(比如解锁后主屏幕加载),会调用onUpdate,这里可以触发一次更新:
@Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); // 触发Work任务更新数据 scheduleWidgetUpdate(context); }
方案3:慎用辅助功能(仅特殊场景)
如果以上方案都满足不了你的实时需求,还有个极端选项:用Accessibility Service监听解锁事件。但这个需要用户手动授权,而且只能用于辅助功能场景,不能滥用——毕竟用户会反感不必要的权限请求。
避坑提醒
- 别想着用前台服务保活进程,Android 12+对前台服务限制极严,还会增加电量消耗,用户很可能直接关掉你的权限。
- 不管用哪种方案,更新Widget时一定要遍历所有已添加的Widget实例,别漏更了用户添加的多个Widget。
内容的提问来源于stack exchange,提问作者Yogesh

