一、核心区别对比
| 特性 | startService() | bindService() |
|---|---|---|
| 启动方式 | 直接启动,无需组件绑定 | 需要与组件(Activity/Fragment)绑定 |
| 生命周期 | onCreate()→onStartCommand() | onCreate()→onBind() |
| 与调用者关系 | 调用者与 Service无直接关联 | 调用者与 Service强绑定 |
| Service 存活 | 调用者销毁后,Service 仍可独立运行 | 所有绑定者解绑后,Service 自动销毁 |
| 通信方式 | 单向:Intent 传入,无直接回调 | 双向:通过IBinder接口直接通信 |
| 典型用途 | 后台长期任务(音乐播放、文件下载) | 需要与 UI 交互的后台服务(音乐控制、定位) |
| 多次调用 | 每次调用都触发onStartCommand() | 首次绑定触发onBind(),后续复用同一连接 |
二、生命周期对比
startService 生命周期
调用 startService() ↓ onCreate() [仅首次] ↓ onStartCommand() [每次 startService() 都会触发] ↓ ... 服务运行中 ... ↓ 调用 stopService() 或 stopSelf() ↓ onDestroy()bindService 生命周期
调用 bindService() ↓ onCreate() [仅首次] ↓ onBind() [返回 IBinder,仅首次绑定触发] ↓ ServiceConnection.onServiceConnected() [回调给调用者] ↓ ... 服务运行中,可双向通信 ... ↓ 所有绑定者调用 unbindService() ↓ onUnbind() [最后一个解绑时触发] ↓ onDestroy()混合模式(同时使用两者)
startService() + bindService() ↓ onCreate() → onStartCommand() → onBind() ↓ ... 运行中 ... ↓ 所有 bind 解绑 + stopService()/stopSelf() ↓ onUnbind() → onDestroy()关键规则:混合模式下,Service 必须同时满足两个条件才会销毁:
- 没有
startService()启动(或已调用stopSelf()/stopService())- 没有活跃的绑定连接
三、代码示例
1. startService 示例
// Service 端publicclassMusicServiceextendsService{@OverridepublicvoidonCreate(){super.onCreate();Log.d("MusicService","onCreate");}@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){Stringaction=intent.getStringExtra("action");if("play".equals(action)){startMusic();}elseif("stop".equals(action)){stopMusic();}// START_STICKY: 被杀死后系统会自动重启returnSTART_STICKY;}@OverridepublicvoidonDestroy(){super.onDestroy();stopMusic();Log.d("MusicService","onDestroy");}@OverridepublicIBinderonBind(Intentintent){returnnull;// startService 不需要 Binder}privatevoidstartMusic(){/* ... */}privatevoidstopMusic(){/* ... */}}// Activity 端启动publicclassMainActivityextendsAppCompatActivity{publicvoidstartMusic(){Intentintent=newIntent(this,MusicService.class);intent.putExtra("action","play");startService(intent);}publicvoidstopMusic(){Intentintent=newIntent(this,MusicService.class);stopService(intent);}}2. bindService 示例
// Service 端:提供 Binder 接口publicclassMusicServiceextendsService{privatefinalIBinderbinder=newMusicBinder();privatebooleanisPlaying=false;// 定义通信接口publicclassMusicBinderextendsBinder{publicMusicServicegetService(){returnMusicService.this;}}// 暴露给外部的方法publicvoidplay(){isPlaying=true;// 播放逻辑}publicvoidpause(){isPlaying=false;// 暂停逻辑}publicbooleanisPlaying(){returnisPlaying;}@OverridepublicIBinderonBind(Intentintent){returnbinder;}}// Activity 端绑定publicclassMainActivityextendsAppCompatActivity{privateMusicServicemusicService;privatebooleanisBound=false;privateServiceConnectionconnection=newServiceConnection(){@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){MusicService.MusicBinderbinder=(MusicService.MusicBinder)service;musicService=binder.getService();isBound=true;// 绑定成功,可以调用 Service 方法updateUI();}@OverridepublicvoidonServiceDisconnected(ComponentNamename){isBound=false;musicService=null;}};@OverrideprotectedvoidonStart(){super.onStart();Intentintent=newIntent(this,MusicService.class);bindService(intent,connection,Context.BIND_AUTO_CREATE);}@OverrideprotectedvoidonStop(){super.onStop();if(isBound){unbindService(connection);isBound=false;}}// 通过 Service 控制音乐publicvoidonPlayClick(Viewv){if(isBound&&musicService!=null){musicService.play();updateUI();}}privatevoidupdateUI(){if(musicService!=null){booleanplaying=musicService.isPlaying();// 更新按钮状态}}}3. 混合模式示例(最常用)
// 先 startService 保证长期存活,再 bindService 实现交互publicclassMainActivityextendsAppCompatActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);// 第一步:startService 保证 Service 不被系统轻易杀死Intentintent=newIntent(this,MusicService.class);startService(intent);// 第二步:bindService 实现双向通信bindService(intent,connection,Context.BIND_AUTO_CREATE);}@OverrideprotectedvoidonDestroy(){super.onDestroy();// 只解绑,不 stopService,让音乐继续后台播放if(isBound){unbindService(connection);}}// 真正退出应用时才 stopServicepublicvoidexitApp(){Intentintent=newIntent(this,MusicService.class);stopService(intent);}}四、日常使用场景
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 后台音乐播放 | startService+bindService | 需要长期运行 + 需要 UI 控制 |
| 文件下载 | startService | 不需要 UI 交互,完成后自动停止 |
| 定位服务 | bindService | 需要实时获取位置数据更新 UI |
| 计步器 | startService | 长期后台运行,不需要频繁交互 |
| 视频播放器后台播放 | 混合模式 | 切后台继续播放,返回前台可控制 |
| AIDL 跨进程通信 | bindService | 必须通过 Binder 实现 IPC |
五、注意事项
1. 内存泄漏(最常见)
// ❌ 错误:匿名内部类持有 Activity 引用publicclassMainActivityextendsAppCompatActivity{privateServiceConnectionconnection=newServiceConnection(){@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){// 这里隐式持有 MainActivity 引用// 如果 Service 生命周期比 Activity 长,会造成内存泄漏}};}// ✅ 正确:使用静态内部类 + WeakReferencepublicclassMainActivityextendsAppCompatActivity{privatestaticclassMyServiceConnectionimplementsServiceConnection{privateWeakReference<MainActivity>activityRef;MyServiceConnection(MainActivityactivity){this.activityRef=newWeakReference<>(activity);}@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){MainActivityactivity=activityRef.get();if(activity!=null){// 安全使用}}@OverridepublicvoidonServiceDisconnected(ComponentNamename){}}}2. 重复解绑崩溃
// ❌ 错误:多次调用 unbindService 会抛 IllegalArgumentException@OverrideprotectedvoidonDestroy(){super.onDestroy();unbindService(connection);// 如果已经解绑过,这里崩溃}// ✅ 正确:用标志位控制@OverrideprotectedvoidonDestroy(){super.onDestroy();if(isBound){unbindService(connection);isBound=false;}}3. bindService 后忘记解绑
// ❌ 错误:在 onDestroy 中不解绑@OverrideprotectedvoidonDestroy(){super.onDestroy();// 漏了 unbindService!}绑定后不解绑会导致:
- Service 无法销毁,占用内存
- Activity 泄漏(ServiceConnection 持有 Activity 引用)
- 应用退出时可能报
ServiceConnectionLeaked警告
4. 混合模式下的生命周期陷阱
// 陷阱:先 bind 后 start,解绑后 Service 不会自动销毁// 因为 startService 启动了它,需要显式 stopService// 正确顺序:// 1. startService() → 启动// 2. bindService() → 绑定// 3. unbindService() → 解绑(Service 继续运行)// 4. stopService() → 真正停止5. 主线程耗时操作
// ❌ 错误:在 Service 主线程做耗时操作@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){downloadLargeFile();// ANR!returnSTART_STICKY;}// ✅ 正确:使用子线程或 IntentService/WorkManager@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){newThread(()->downloadLargeFile()).start();returnSTART_STICKY;}6. Android 8.0+ 后台限制
从 Android 8.0(API 26)开始,后台应用调用startService()会受到限制:
// Android 8.0+ 后台启动 Service 会抛 IllegalStateException// 解决方案:使用 startForegroundService()if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){startForegroundService(intent);}else{startService(intent);}// 然后在 Service 的 onCreate() 中必须在 5 秒内调用 startForeground()@OverridepublicvoidonCreate(){super.onCreate();Notificationnotification=buildNotification();startForeground(NOTIFICATION_ID,notification);}7. onStartCommand 返回值选择
@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){// 根据场景选择返回值:// START_STICKY:服务被杀后自动重启,intent 为 null(音乐播放)// START_NOT_STICKY:服务被杀后不自动重启(一次性任务)// START_REDELIVER_INTENT:服务被杀后自动重启,且重新传入原 intent(下载任务)returnSTART_STICKY;}六、总结
| 问题 | 答案 |
|---|---|
| 需要后台长期运行且不与 UI 交互? | 用startService() |
| 需要与 Activity 实时交互? | 用bindService() |
| 既要后台运行又要 UI 控制? | 混合模式:先startService()再bindService() |
| 混合模式下如何正确销毁? | 先unbindService()再stopService() |
| Android 8.0+ 后台启动限制? | 用startForegroundService()+startForeground() |
核心原则:
startService管"活多久",bindService管"怎么交互"。两者不是互斥的,而是互补的,混合使用是最常见的实战模式。