13、广播接收器与服务:BroadcastReceiver静态与动态注册、自定义广播、Service生命周期与启动方式、IntentService与WorkManager
广播接收器和服务,这两个东西在Android里就像一对老搭档。一个负责“喊话”,一个负责“干活”。我刚开始学的时候,总觉得它们有点抽象,尤其是广播,感觉像在玩对讲机。但用多了你会发现,它们其实是解耦和后台任务的利器。
13.1 广播接收器:两种注册方式
广播接收器,说白了就是监听系统或应用发出的“消息”。比如电量低了、网络变了、屏幕亮了,系统都会“喊一嗓子”。你的接收器如果注册了对应的“频道”,就能听到并做出反应。
注册方式有两种:静态注册和动态注册。嗯,这里有个坑,我当年踩过。
13.1.1 静态注册
静态注册是在 AndroidManifest.xml 里声明的。应用安装时,系统就记住了这个接收器。哪怕应用没在运行,广播来了也能唤醒它。
举个例子,监听开机完成广播:
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
对应的Java代码:
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 开机后,你想干点啥?
Toast.makeText(context, "手机开机了!", Toast.LENGTH_SHORT).show();
}
}
13.1.2 动态注册
动态注册是在代码里用 registerReceiver() 完成的。它的生命周期跟着组件走,Activity或Service销毁了,记得要 unregisterReceiver(),否则会内存泄漏。
我习惯在 onResume() 里注册,onPause() 里注销。这样最安全。
// 注册
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_LOW);
registerReceiver(batteryReceiver, filter);
// 注销(别忘了!)
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(batteryReceiver);
}
13.2 自定义广播:自己发消息
除了听系统的,你也可以自己发广播。这在组件间通信时特别好用。比如A页面做了个操作,想让B页面知道,但又不想用EventBus这种第三方库。
发送自定义广播:
Intent intent = new Intent("com.example.MY_CUSTOM_ACTION");
intent.putExtra("data", "你好,广播!");
sendBroadcast(intent);
接收端注册:
IntentFilter filter = new IntentFilter("com.example.MY_CUSTOM_ACTION");
registerReceiver(myReceiver, filter);
个人经验:自定义广播的action字符串,最好加上包名前缀,比如 com.example.app.MY_ACTION。否则容易跟其他应用的广播冲突。我见过一个项目,两个第三方SDK用了同一个action名字,结果广播互相干扰,调试了一整天。
13.3 Service:后台的“长工”
Service是Android里做后台任务的组件。它没有界面,默默在后台运行。你想想看,下载文件、播放音乐、上传日志,这些活儿都适合交给Service。
13.3.1 生命周期
Service的生命周期比Activity简单多了。主要就这几个回调:
onCreate():创建时调用,只调用一次。onStartCommand():每次startService()都会调用。onBind():绑定服务时调用。onDestroy():销毁时调用。
我画了个图,帮你理清关系:
13.3.2 启动方式
Service有两种启动方式,说白了就是“两种用法”:
| 启动方式 | 方法 | 特点 | 适用场景 |
|---|---|---|---|
| 启动式 | startService() |
独立运行,不依赖调用者。调用者退出,Service还在跑。 | 下载、播放音乐 |
| 绑定式 | bindService() |
与调用者绑定,调用者销毁,Service跟着销毁。 | 需要交互、获取数据 |
我个人习惯:如果只是后台默默干活,用 startService()。如果需要跟Service通信(比如获取下载进度),就用 bindService()。也可以两者结合,先启动再绑定。
13.4 IntentService:自动停的“好帮手”
普通Service有个麻烦:它运行在主线程。如果你在里面做耗时操作,必须自己开子线程。而且做完后,还得手动 stopSelf() 停止。否则Service会一直挂着。
IntentService就是来解决这个痛点的。它内部自带工作线程,任务执行完自动停止。说白了,就是“即用即走”。
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
// 这里已经在子线程了,放心做耗时操作
String action = intent.getStringExtra("action");
Log.d("TAG", "处理任务: " + action);
}
}
避坑指南:IntentService在Android 8.0之后被标记为废弃了。虽然还能用,但官方推荐用 JobIntentService 或 WorkManager。我建议新项目直接上WorkManager,别再用老古董了。
13.5 WorkManager:现代后台任务方案
WorkManager是Google推荐的现代后台任务库。它最大的优点是:保证任务执行。哪怕应用被杀、手机重启,任务也会在条件满足时重新执行。
你想想看,以前用Service做后台上传,用户一划杀应用,任务就丢了。WorkManager不会,它会记住任务,等下次开机或网络恢复时继续执行。
基本用法:
// 定义任务
OneTimeWorkRequest uploadWork = new OneTimeWorkRequest.Builder(UploadWorker.class)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 需要网络
.build())
.build();
// 提交任务
WorkManager.getInstance(context).enqueue(uploadWork);
Worker类:
public class UploadWorker extends Worker {
public UploadWorker(Context context, WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
// 在这里做上传操作
return Result.success(); // 返回成功
}
}
13.6 总结对比
最后,我整理了一个对比表,方便你选型:
| 组件 | 适用场景 | 生命周期 | 推荐指数 |
|---|---|---|---|
| BroadcastReceiver | 监听系统广播、组件间通信 | 短,onReceive执行完就结束 | ⭐⭐⭐⭐ |
| Service | 后台持续运行(音乐、定位) | 长,需手动停止 | ⭐⭐⭐ |
| IntentService | 一次性后台任务(已废弃) | 自动停止 | ⭐ |
| WorkManager | 可延迟、需保证完成的后台任务 | 由系统调度 | ⭐⭐⭐⭐⭐ |
嗯,广播和服务这块内容不少,但核心就这几个点。你只要记住:广播是“喊话”,Service是“干活”,WorkManager是“靠谱的管家”。项目中多练几次,自然就熟了。
公众号:蓝海资料掘金营,微信deep3321