为何静态Broadcast Receiver无法响应ACTION_POWER_CONNECTED/DISCONNECTED事件?
解决静态Broadcast Receiver无法监听充电状态的问题
兄弟,你遇到的这个问题其实是Android系统从8.0(API 26)开始的隐式广播限制导致的——不是你配置错了,是系统直接不让静态注册的接收器收这类广播了。
为什么静态注册失效?
Android 8.0为了优化电池续航,砍掉了大部分隐式广播的静态注册支持,目的是减少后台App不必要的唤醒。而ACTION_POWER_CONNECTED和ACTION_POWER_DISCONNECTED正好属于被限制的范畴,所以哪怕你在Manifest里写了<receiver>配置,静态接收器也完全收不到这两个广播信号。
不过别慌,有两个靠谱的替代方案可以实现你“Activity未打开时也能响应充电状态”的需求:
方案1:Foreground Service + 动态注册广播接收器
这个方案能让你的App在后台持续监听充电状态,而且因为是前台服务,系统很难把它杀掉。
步骤1:写一个Foreground Service
public class ChargingMonitorService extends Service { private BroadcastReceiver chargingReceiver; @Override public void onCreate() { super.onCreate(); // 动态注册充电广播接收器 chargingReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action == null) return; String toastMsg = action.equals(Intent.ACTION_POWER_CONNECTED) ? "嘿,设备连上充电了!" : "哎呀,充电断了~"; Toast.makeText(context, toastMsg, Toast.LENGTH_LONG).show(); } }; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED); registerReceiver(chargingReceiver, filter); // 把服务设为前台服务,避免被系统回收 NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "charging_channel") .setSmallIcon(R.drawable.ic_baseline_battery_charging_24) .setContentTitle("充电监听中") .setContentText("正在跟踪你的充电状态") .setPriority(NotificationCompat.PRIORITY_LOW); // Android 8.0+必须创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( "charging_channel", "充电状态通知", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); if (manager != null) manager.createNotificationChannel(channel); } startForeground(1001, builder.build()); } @Override public void onDestroy() { super.onDestroy(); // 记得注销接收器,避免内存泄漏 if (chargingReceiver != null) { unregisterReceiver(chargingReceiver); } } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
步骤2:在Manifest里配置权限和Service
<!-- Android 9+需要前台服务权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- 可选:开机自动启动服务,重启后继续监听 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application ...> <!-- 声明前台服务 --> <service android:name=".ChargingMonitorService" android:foregroundServiceType="dataSync" /> <!-- Android 12+需要指定类型 --> <!-- 可选:开机启动接收器 --> <receiver android:name=".BootCompletedReceiver"> <intent-filter> <action android:name="android.intent.action.ACTION_BOOT_COMPLETED" /> </intent-filter> </receiver> </application>
步骤3:写开机启动的接收器(可选)
如果想让设备重启后自动开始监听,就加这个接收器:
public class BootCompletedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { Intent serviceIntent = new Intent(context, ChargingMonitorService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); } } } }
步骤4:启动服务
在你的MainActivity里加一行代码,确保App第一次打开时启动服务:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent serviceIntent = new Intent(this, ChargingMonitorService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } }
方案2:用WorkManager监听充电状态
如果你的需求不是即时弹Toast,而是在充电状态变化时执行一些后台任务(比如同步数据、上传文件),那WorkManager是更推荐的方案——它会自动根据系统资源和电池情况优化任务执行时机,更省电。
步骤1:加WorkManager依赖
在Module级别的build.gradle里添加:
dependencies { implementation "androidx.work:work-runtime:2.8.1" }
步骤2:写Worker类
public class ChargingWorker extends Worker { public ChargingWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @NonNull @Override public Result doWork() { // 判断当前充电状态 BatteryManager batteryManager = getApplicationContext().getSystemService(BatteryManager.class); boolean isCharging = batteryManager.isCharging(); String message = isCharging ? "设备正在充电,开始执行后台任务~" : "充电停止了,暂停后台任务"; // WorkManager在后台线程,弹Toast要切回主线程 new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show() ); return Result.success(); } }
步骤3:调度任务
在App启动时(比如MainActivity的onCreate)设置任务触发条件:
// 设置约束:当充电状态变化时触发 Constraints chargingConstraints = new Constraints.Builder() .setRequiresCharging(true) // 充电时触发 .build(); Constraints notChargingConstraints = new Constraints.Builder() .setRequiresCharging(false) // 停止充电时触发 .build(); // 创建两个任务,分别监听充电和停止充电 OneTimeWorkRequest chargingWork = new OneTimeWorkRequest.Builder(ChargingWorker.class) .setConstraints(chargingConstraints) .build(); OneTimeWorkRequest stopChargingWork = new OneTimeWorkRequest.Builder(ChargingWorker.class) .setConstraints(notChargingConstraints) .build(); // 加入任务队列 WorkManager.getInstance(this).enqueue(chargingWork); WorkManager.getInstance(this).enqueue(stopChargingWork);
总结一下:静态注册充电广播在Android 8.0+已经彻底失效,必须用动态注册结合前台服务,或者WorkManager来实现后台监听的需求。
内容的提问来源于stack exchange,提问作者justMe




