You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

为何静态Broadcast Receiver无法响应ACTION_POWER_CONNECTED/DISCONNECTED事件?

解决静态Broadcast Receiver无法监听充电状态的问题

兄弟,你遇到的这个问题其实是Android系统从8.0(API 26)开始的隐式广播限制导致的——不是你配置错了,是系统直接不让静态注册的接收器收这类广播了。

为什么静态注册失效?

Android 8.0为了优化电池续航,砍掉了大部分隐式广播的静态注册支持,目的是减少后台App不必要的唤醒。而ACTION_POWER_CONNECTEDACTION_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

火山引擎 最新活动