23、Service与后台任务:Service生命周期、IntentService、前台Service、Android 8.0后台限制
Service,说白了就是Android里跑在后台的“隐形人”。没有界面,不跟用户直接打交道,但能干很多脏活累活——下载文件、播放音乐、上传日志……嗯,我刚开始学Android时,总觉得Service挺神秘的,后来踩了几个坑才明白,它其实没那么复杂,但规矩不少。
Service的生命周期:不是你想活多久就活多久
Service的生命周期,说白了就两套回调:startService 和 bindService。你启动它的方式不同,它活着的逻辑也不同。
- startService:一调用,Service就“独立”了。哪怕启动它的Activity被销毁了,Service还在后台跑着。直到你主动调
stopSelf或别人调stopService,它才会走onDestroy。 - bindService:这是“绑定模式”。Service跟调用者同生共死。调用者(比如Activity)一销毁,Service就自动解绑并销毁。适合那种“你在我就干活,你走我就停”的场景。
我个人习惯:如果只是后台下载个文件,用startService;如果需要跟Service交互(比如获取下载进度),就用bindService。但要注意,两者可以混用——先start再bind,这时候Service会一直活着,直到所有绑定都解除了,并且你调了stopSelf。
生命周期回调顺序:
- startService → onCreate() → onStartCommand() → onDestroy()
- bindService → onCreate() → onBind() → onUnbind() → onDestroy()
我在项目中遇到过一个问题:Service在后台跑着,用户把App滑掉,Service竟然被杀了。后来才发现,是系统在内存紧张时回收了它。嗯,这里要注意——Service不是守护进程,系统有权杀它。想让它更稳?用前台Service。
IntentService:自动收工的“好员工”
IntentService,说白了就是Service的“简化版”。它自带一个工作线程,任务执行完自动调用stopSelf。你想想看,普通Service你得自己开线程,自己管理停止,稍不注意就内存泄漏。IntentService把这些脏活都包了。
我记得第一次用IntentService时,觉得它太贴心了。你只需要实现 onHandleIntent 方法,把耗时操作扔进去就行。它内部有个HandlerThread,任务排队执行,一个接一个,不会并发乱掉。
public class MyDownloadService extends IntentService {
public MyDownloadService() {
super("MyDownloadService");
}
@Override
protected void onHandleIntent(Intent intent) {
// 这里已经在子线程了,直接干耗时活
String url = intent.getStringExtra("url");
downloadFile(url);
}
}
但注意,IntentService在Android 8.0之后被标记为废弃了。为什么?因为它的后台行为跟新系统限制有冲突。官方推荐用 JobIntentService 或直接上 WorkManager。我个人建议:新项目就别用IntentService了,直接用WorkManager更省心。
前台Service:给系统一个“不杀你”的理由
前台Service,说白了就是“刷存在感”的Service。它会在通知栏显示一个持续通知,告诉用户“嘿,我在干活呢,别杀我”。系统看到前台Service,会把它当成“重要进程”,不到万不得已不会杀。
我做过一个音乐播放器,就是用前台Service来保持播放。用户切到其他App,音乐不能停。这时候普通Service很容易被系统回收,但前台Service就稳得很。
启动前台Service的步骤:
- 创建通知渠道(Android 8.0+必须)
- 构建通知对象
- 调用
startForeground(notificationId, notification)
// 在Service的onCreate或onStartCommand中
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("下载中")
.setContentText("正在下载文件...")
.setSmallIcon(R.drawable.ic_download)
.build();
startForeground(1, notification);
但要注意,前台Service的通知不能取消,除非Service停止。用户如果觉得烦,可以手动关掉通知,但Service会跟着被系统杀掉。嗯,这里有个坑——Android 9.0之后,前台Service需要申请权限(FOREGROUND_SERVICE),别忘了在Manifest里加上。
Android 8.0后台限制:系统开始“管闲事”了
Android 8.0(API 26)是个分水岭。从这版本开始,系统对后台Service下了狠手。说白了,就是不让App在后台随便浪了。
- 后台Service被限制启动:App处于后台时,不能通过startService启动Service。系统会直接抛异常。但前台Service不受影响。
- 隐式广播被砍:大部分隐式广播(比如网络变化)不再能唤醒你的Service。你得用显式广播或JobScheduler。
- JobScheduler上位:系统推荐用JobScheduler来替代后台Service。它会把任务交给系统统一调度,省电又高效。
我曾经维护过一个老项目,升级targetSdkVersion到26后,后台Service全炸了。用户反馈说“App怎么不下载了?”排查半天,发现是后台Service启动时直接崩溃。解决方案就是:能转前台Service就转,不能转就用JobScheduler或WorkManager。
避坑指南:
我曾经在Android 8.0手机上测试后台Service,发现即使用了startForeground,如果App被系统划入“后台”状态超过几分钟,Service还是会被杀。后来查文档才知道,系统会定期清理后台App的进程。所以,关键任务一定要用前台Service+高优先级通知,并且做好被杀的恢复逻辑。
知识体系:一张图看懂Service与后台任务
这张图把Service的三种形态和Android 8.0的限制串起来了。你看,普通Service和IntentService在8.0之后都受到了限制,只有前台Service还能“硬刚”。但前台Service也不是万能药——它需要通知,而且用户可能反感。所以,能不用Service就不用,用WorkManager才是正道。
总结一下
Service是Android后台任务的基石,但系统越来越严。我的建议是:
- 简单后台任务(比如日志上传)→ 用WorkManager
- 需要持续运行的任务(比如音乐播放)→ 用前台Service
- 老项目迁移 → 把IntentService换成JobIntentService或WorkManager
嗯,Service这块内容就这些。记住一句话:别跟系统对着干,它限制你,你就用它的推荐方案。这样你的App才能活得久、跑得稳。